Skip to content
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

promote_rule: forbid method definitions which may cause ambiguity for unknown types #55866

Open
nsajko opened this issue Sep 25, 2024 · 0 comments
Labels
design Design of APIs or of the language itself docs This change adds or pertains to documentation

Comments

@nsajko
Copy link
Contributor

nsajko commented Sep 25, 2024

Suppose a type T is defined in a package. Further suppose that S is some type not owned by the same package, and T <: S. Defining either of the following method signatures should be forbidden, because it causes dispatch ambiguity for perfectly valid promote_rule definitions from third-party packages:

Base.promote_rule(::Type{  T}, ::Type{<:S})
Base.promote_rule(::Type{<:T}, ::Type{<:S})
Base.promote_rule(::Type{<:S}, ::Type{  T})
Base.promote_rule(::Type{<:S}, ::Type{<:T})

NB: Base doesn't respect this rule, though perhaps this is OK/less bad, given that Base is an implicit dependency of everything anyway. Examples:

promote_rule(::Type{BigInt}, ::Type{<:Integer}) in Base.GMP at gmp.jl:460
promote_rule(::Type{BigFloat}, ::Type{<:Real}) in Base.MPFR at mpfr.jl:366
promote_rule(::Type{Bool}, ::Type{T}) where T<:Number @ Base bool.jl:4
promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where T<:Real @ Base irrationals.jl:47
promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational, T<:Number} @ Base irrationals.jl:48

Example code for ambiguity errors:

# package A
struct TA <: Integer end
Base.promote_rule(::Type{TA}, t::Type{<:Real}) = promote_type(Int, t)  # offending definition
Base.promote_rule(t::Type{<:Real}, ::Type{TA}) = promote_type(Int, t)  # offending definition

# package B
struct TB <: AbstractFloat end
Base.promote_rule(::Type{TB}, t::Type{<:Integer}) = promote_type(Float64, t)  # OK
julia> promote_type(TA, TB)
ERROR: MethodError: promote_rule(::Type{TB}, ::Type{TA}) is ambiguous.

Candidates:
  promote_rule(::Type{TB}, t::Type{<:Integer})
    @ Main REPL[5]:1
  promote_rule(t::Type{<:Real}, ::Type{TA})
    @ Main REPL[3]:1

Possible fix, define
  promote_rule(::Type{TB}, ::Type{TA})

Stacktrace:
 [1] promote_type(::Type{TA}, ::Type{TB})
   @ Base ./promotion.jl:318
 [2] top-level scope
   @ REPL[6]:1

julia> using Test

julia> detect_ambiguities(Main, recursive=true)
8-element Vector{Tuple{Method, Method}}:
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{BigInt}, ::Type{<:Integer}) @ Base.GMP gmp.jl:480)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{Bool}, ::Type{T}) where T<:Number @ Base bool.jl:4)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer, S<:Integer} @ Base rational.jl:180)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{TB}, t::Type{<:Integer}) @ Main REPL[5]:1)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where T<:Real @ Base irrationals.jl:47)
 (promote_rule(::Type{TA}, t::Type{<:Real}) @ Main REPL[2]:1, promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational, T<:Number} @ Base irrationals.jl:48)
 (promote_rule(t::Type{<:Real}, ::Type{TA}) @ Main REPL[3]:1, promote_rule(::Type{BigFloat}, ::Type{<:Real}) @ Base.MPFR mpfr.jl:478)
@nsajko nsajko added docs This change adds or pertains to documentation design Design of APIs or of the language itself labels Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design of APIs or of the language itself docs This change adds or pertains to documentation
Projects
None yet
Development

No branches or pull requests

1 participant