-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Unclear ambiguity when one argument is more specific in one method and another argument in the other #47385
Comments
one of the union has but yeah I would expect Union is equivalent to splitting into n methods so I wouldn't expect an ambiguity error here . |
Not sure how that's relevant - the ambiguity error is thrown when passing in |
Because it IS relevant? julia> struct Foo{N} <: IO end
julia> Base.write(::Foo{N}, b::Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}) where N = write(stdout, b)
julia> write(Foo{0x0}(), 0x0000)
2
#btw
julia> Base.write(::Foo{N}, b::Union{Int8, UInt8}) where N = write(stdout, b)
julia> write(Foo{0x0}(), 0x00)
ERROR: MethodError: write(::Foo{0x00}, ::UInt8) is ambiguous. Candidates:
write(s::IO, x::UInt8) in Base at io.jl:278
write(::Foo{N}, b::Union{Int8, UInt8}) where N in Main at REPL[6]:1
Possible fix, define
write(::Foo{N}, ::UInt8) where N |
What I'm saying is that I don't see how the julia> un
Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}
julia> typeintersect(Base.BitInteger, un)
Union{Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}
julia> typeintersect(un, UInt16)
UInt16
julia> typeintersect(Base.BitInteger, UInt16)
UInt16 Both contain julia> Base.morespecific(Foo, IO)
true
julia> Base.morespecific(Tuple{Foo, UInt16}, Tuple{IO, UInt16})
true I guess this indicates a problem with method selection in dispatch? I would have thought that julia> args = Tuple{Foo{0x0}, UInt16}
Tuple{Foo{0x00}, UInt16}
julia> io_sig = Tuple{IO, un}
Tuple{IO, Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}}
julia> foo_sig = Tuple{Foo, Base.BitInteger}
Tuple{Foo, Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}}
julia> typeintersect(io_sig, args)
Tuple{Foo{0x00}, UInt16}
julia> typeintersect(foo_sig, args)
Tuple{Foo{0x00}, UInt16} but maybe that's not the case and is done deliberately this way. The |
not saying it should, but it does, and since we don't have a formal spec, the only spec is the implementation. |
Yeah, and I'm saying we should change the implementation to make this clearly unambiguous case truly not ambiguous :) |
So ... the suggestion is that if there's ambiguity as now, i.e. the typeintersect of the candidates' type signatures is not equal to any of them, one should check if one of the signatures has more concrete types than the others and choose that one? |
No, not necessarily. The signature containing So for two methods |
I suspect morespecific is struggling to prove that all elements of both Unions are equivalent-or-disjoint, so it is giving up and declaring ambiguity. It would like to try to sort the method such that it has the same precedence as if each element of the Union was declared as a separate method. But comparing Unions then is tricky, since it has to decide if all elements of one are strictly ordered (<=) with the elements of the other one. (c.f. the start of my JuliaCon 2022 talk) This seems likely a fixable case. |
For The way I picture dispatch to work (and this may be totally wrong) is that the function to-be-called determines the method table to use. Then, a filtering process starts - if at the end of that filtering process we have more than one method left over, we have an ambiguity. If no methods are left over, we have a I think the current steps for that filtering are (roughly)
And what I think it should be
It might be possible to have a pre-filtering step where you just count the number of concrete, |
See also #47325 which would resolve this ambiguity in favor of the |
Yes, partially - I think this issue can be fixed without impacting the more general case presented there (what I still call a "true ambiguity" here). |
This has regressed now - on master, the first julia> versioninfo()
Julia Version 1.10.0-DEV.103
Commit d3e4daba80 (2022-12-14 19:36 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: 24 × AMD Ryzen 9 7900X 12-Core Processor
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-14.0.6 (ORCJIT, znver3)
Threads: 24 on 24 virtual cores
Environment:
JULIA_NUM_THREADS = 24
julia> struct Foo{N} <: IO end
julia> Base.write(::Foo{N}, b::Base.BitInteger) where N = write(stdout, b)
julia> write(Foo{0x0}(), 0x00)
ERROR: MethodError: write(::Foo{0x00}, ::UInt8) is ambiguous.
Candidates:
write(s::IO, x::UInt8)
@ Base io.jl:291
write(::Foo{N}, b::Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}) where N
@ Main REPL[3]:1
Possible fix, define
write(::Foo{N}, ::UInt8) where N
Stacktrace:
[1] top-level scope
@ REPL[4]:1
julia> write(Foo{0x0}(), 0x0000)
ERROR: MethodError: write(::Foo{0x00}, ::UInt16) is ambiguous.
Candidates:
write(s::IO, x::Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64})
@ Base io.jl:688
write(::Foo{N}, b::Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}) where N
@ Main REPL[3]:1
Possible fix, define
write(::Foo{N}, ::Union{Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64}) where N
Stacktrace:
[1] top-level scope
@ REPL[5]:1 As far as I can tell, no new methods have been added, so it's unclear where this regressed.. |
Looks like a bugfix. You should not be able to define two methods simultaneously for Uint8 |
Not sure what you mean - I don't think I've done that? The two errors are from distinct calls, they're just the MWE from the initial post. I tried bisecting, without much success though.. |
Sorry, yeah, I meant the royal you, since Base defined one of the methods and the OP defined the other |
But I am the OP, and only Base is defining the fallback for UInt8 😅 I'm not defining a method for |
MWE:
I think this should not be ambigious, since
Foo{0x0}
is concrete and more specific thanIO
, while the intersection of the twoUnion
s with the passed in type is the same, so the second argument cannot serve as a tie breaker/cause for ambiguity either way.Version:
The text was updated successfully, but these errors were encountered: