Interesting behaviour with parametric types in inner constructors

Hi all,
I recently stumbled over an interesting behaviour regarding parametric types used in inner constructors. The short version is, that this code

struct TestStruct1{Tx<:Integer,Ty<:Float64}
    x::Tx
    y::Ty
    function TestStruct1{Ty}(y::Ty) where {Ty<:Float64}
        x = 0
        return new{typeof(x),Ty}(x,y)
    end
end
TestStruct1{Float64}(0.0)

throws an error without even calling the constructor, namely

ERROR: TypeError: in TestStruct1, in Tx, expected Tx<:Integer, got Type{Float64}

The line given in the error text is the call of TestStruct1 at the bottom.

However this code

struct TestStruct2{Ty<:Float64,Tx<:Integer}
    x::Tx
    y::Ty
    function TestStruct2{Ty}(y::Ty) where {Ty<:Float64}
        x = 0
        return new{Ty,typeof(x)}(x,y)
    end
end
TestStruct2{Float64}(0.0)

where only Ty and Tx are switched in the curly braces, works fine. After some trial and error I found out, that for struct TestStruct{T1,...,Tn} only inner constructors of the form function TestStruct{T1,...,Tk} will be run correctly, anything out of order or skipping a Ti will break the code.
Is this behaviour intended?

Hi!

Type parameters in a struct are not named. Thus, TestStruct1{Ty} in both cases implies Ty as the first type parameter. Since in the first case you explicitly limited it to <:Integer, you get an error — Julia doesn’t allow you to define a constructor with a signature like this. This behavior is intended, because it’s not entirely clear what to do with the type parameters you skipped, and this implicit behavior is potentially error-prone.

Sidenote: You probably want Ty<:Real instead, because Float64 is a concrete type.