abstract type XX{A <: Real} end
function makexx(T::Type{<:XX})
T(6.0)
end
struct X1{A} <: XX{A}
x::A
end
struct X2{A} <: XX{A} end
X1() = makexx(X1)
X1()
Crashes with:
MethodError: no method matching makexx(::Type{X1})
Interesting case. Note that this works when using the XX{A<:Real} pattern:
julia> makexx(X1{Float64})
X1{Float64}(6.0)
but I don’t know why it fails with X1 only.
My guess is that when you call the function with makexx(X1) you are implicitly doing makexx(X1{Any}), and there is no method in that case to dispatch, like this:
julia> makexx(X1{Any})
ERROR: TypeError: in XX, in A, expected A<:Real, got Type{Any}
Stacktrace:
[1] top-level scope
@ REPL[7]:1
but with a confusing error message, because you only get the type error for an anonymous type.
Note that this also solves the problem, by removing the possibility of interpreting X1 as something which is not a subtype of XX:
julia> abstract type XX{A <: Real} end
julia> struct X1{A<:Real} <: XX{A} x::A end # note the <: Real here
julia> makexx(T::Type{<:XX}) = 1
makexx (generic function with 1 method)
julia> makexx(X1)
1
That is, the problem arises when it is possible to have a X1 which is not a subtype of XX{A<:Real}. Then the function signature gets confused. Maybe it is possible to provide a better error message there?
To add to @lmiq’s anwser: This is an open issue (#37901). The problem is
julia> X1 <: XX
false
Basically, XX is equal to XX{<:Real} but X1 is equal to X1{<:Any}. So X1 also includes types like X1{String} (even though such a type cannot be instantiated), which is not a subtype of XX.
julia> X1 <: XX{<:Any}
true
julia> X1{<:Real} <: XX
true
Like @lmiq said, the easiest solution would be to define
struct X1{A<:Real} <: XX{A}
x::A
end
struct X2{A<:Real} <: XX{A} end
(with A<:Real instead of just A). In that case you have X1 <: XX so that makexx(X1) will work.