Julia: Possible bug: `g(1, String)` matches the method signature `g(c::C, d::Type{D}) where D <: C where C`

Created on 16 Feb 2020  Â·  4Comments  Â·  Source: JuliaLang/julia

I'm not sure if this is a bug or not.

First, define the functions f and g as follows:

julia> function f(a::A, b::Type{B}) where A <: B where B
           println("a: ", a)
           println("b: ", b)
           println("A: ", A)
           println("B: ", B)
           return nothing
       end
f (generic function with 1 method)

julia> function g(c::C, d::Type{D}) where D <: C where C
           println("c: ", c)
           println("d: ", d)
           println("D: ", D)
           println("C: ", C)
           return nothing
       end
g (generic function with 1 method)

julia> methods(f)
# 1 method for generic function "f":
[1] f(a::A, b::Type{B}) where {B, A<:B} in Main at REPL[1]:2

julia> methods(g)
# 1 method for generic function "g":
[1] g(c::C, d::Type{D}) where {C, D<:C} in Main at REPL[2]:2

Calling f(1, Int) gives the correct output:

julia> f(1, Int)
a: 1
b: Int64
A: Int64
B: Int64

Calling f(1, Signed) gives the correct output:

julia> f(1, Signed)
a: 1
b: Signed
A: Int64
B: Signed

Calling f(1, String) correctly gives a MethodError, which makes sense because Int is not a subtype of String:

julia> f(1, String)
ERROR: MethodError: no method matching f(::Int64, ::Type{String})
Closest candidates are:
  f(::A, ::Type{B}) where {B, A<:B} at REPL[1]:2
Stacktrace:
 [1] top-level scope at REPL[10]:1

Calling g(1, Int) gives the correct output:

julia> g(1, Int)
c: 1
d: Int64
D: Int64
C: Int64

Now, here is the possible bug. If I understand correctly, g(1, String) should throw a MethodError because String is not a subtype of Int. Instead, however, calling g(1, String) calls the method with type signature g(::Int64, ::Type{String}) (a method that should not exist), and then throws the error ERROR: UndefVarError: C not defined when trying to run the line println("C: ", C).

julia> g(1, String)
c: 1
d: String
D: String
ERROR: UndefVarError: C not defined
Stacktrace:
 [1] g(::Int64, ::Type{String}) at ./REPL[2]:5
 [2] top-level scope at REPL[20]:1

Why does the call g(1, String) match the method g(c::C, d::Type{D}) where D <: C where C? It seems to me that C should be Int and D should be String, and clearly it is not the case that String is a subtype of Int.

Most helpful comment

Yes, that's it. The behavior may be unexpected here, but is in fact correct.

All 4 comments

If the type of the first argument is constrained, then a MethodError is thrown when String is passed as the second argument. However, when Signed is passed as the second argument then the type of the first argument is not defined.

julia> function h(e::E, f::Type{F}) where F <: E where E <: Integer
                  println("e: ", e)
                  println("f: ", f)
                  println("E: ", E)
                  println("F: ", F)
                  return nothing
              end
h (generic function with 1 method)
julia> h(1,Int64)
e: 1
f: Int64
E: Int64
F: Int64

julia> h(1,String)
ERROR: MethodError: no method matching h(::Int64, ::Type{String})
Closest candidates are:
  h(::E, ::Type{F}) where {E<:Integer, F<:E} at REPL[19]:2
Stacktrace:
 [1] top-level scope at REPL[22]:1

julia> h(1,Signed)
e: 1
f: Signed
ERROR: UndefVarError: E not defined
Stacktrace:
 [1] h(::Int64, ::Type{Signed}) at ./REPL[19]:4
 [2] top-level scope at REPL[23]:1

Why does the call g(1, String) match the method g(c::C, d::Type{D}) where D <: C where C?

IIUC this is because any C s.t. C >: Union{String,Int} satisfies this constraint. For example, C = Any is _a_ valid answer. Dispatch is decoupled from type parameter resolution and julia does not try to find the smallest possible type and keep it unbound. If you want to auto-detect something like this, Test package exports detect_unbound_args.

Yes, that's it. The behavior may be unexpected here, but is in fact correct.

@tkf that explains it perfectly, thank you so much!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StefanKarpinski picture StefanKarpinski  Â·  3Comments

StefanKarpinski picture StefanKarpinski  Â·  3Comments

felixrehren picture felixrehren  Â·  3Comments

manor picture manor  Â·  3Comments

TotalVerb picture TotalVerb  Â·  3Comments