From 978338a75860e6bc187d373307973ff9ad866fb6 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 23 Feb 2025 13:32:21 +0530 Subject: [PATCH 1/2] Imformative error in promotion with conflicting promote_rules --- base/promotion.jl | 15 +++++++++++++++ test/core.jl | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/base/promotion.jl b/base/promotion.jl index 72257f8ba5a3d..247a96f75c9a3 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -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 +# 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...) diff --git a/test/core.jl b/test/core.jl index fd607cc86f1d5..7b972f2de9479 100644 --- a/test/core.jl +++ b/test/core.jl @@ -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 "promote_type(PromoteA, PromoteB) failed" promote_type(PromoteA, PromoteB) + @test_throws "promote_type(PromoteB, PromoteA) failed" 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 From ece1461769362b25e11e14577d2fcb29b8bb8fd1 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sun, 23 Feb 2025 20:04:17 +0530 Subject: [PATCH 2/2] Test type of error --- test/core.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core.jl b/test/core.jl index 7b972f2de9479..ccc79a536cf83 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8480,8 +8480,8 @@ 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 "promote_type(PromoteA, PromoteB) failed" promote_type(PromoteA, PromoteB) - @test_throws "promote_type(PromoteB, PromoteA) failed" promote_type(PromoteB, PromoteA) + @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