Repeated type parameter in method error of promoting constructor

Consider the type with the promoting constructor

struct Foo{T,TX<:AbstractVector{T},TFX}
    Δ::T
    x::TX
    fx::TFX
    residual_norm::T
    converged::Bool
    iterations::Int
end

function Foo(Δ::T1, x::AbstractVector{T2}, fx, residual_norm::T3, converged,
                           iterations) where {T1 <: Real, T2 <: Real, T3 <: Real}
    T = promote_type(T1, T2, T3)
    Foo(T(Δ), T.(x), fx, T(residual_norm), converged, iterations)
end

but

julia> Foo(1, [1.0, 2.0], nothing, 1.0, true, 0)
ERROR: MethodError: Foo(::Float64, ::Array{Float64,1}, ::Nothing, ::Float64, ::Bool, ::Int64) is ambiguous. Candidates:
  (::Type{Foo})(Δ::T, x::TX, fx::TFX, residual_norm::T, converged::Bool, iterations::Int64) where {T, TX<:AbstractArray{T,1}, TFX} in Main at REPL[1]:2
  (::Type{Foo})(Δ::T1, x::AbstractArray{T2,1}, fx, residual_norm::T3, converged, iterations) where {T1<:Real, T2<:Real, T3<:Real} in Main at REPL[2]:3
Possible fix, define
  Foo(::T1, ::Union{TX, TX}, ::TFX, ::Union{T3, T}, ::Bool, ::Int64) where {T1<:Real, T2<:Real, T3<:T2, TX<:AbstractArray{T2,1}, T<:Real, TX<:AbstractArray{T,1}, TFX}
Stacktrace:
 [1] Foo(::Int64, ::Array{Float64,1}, ::Nothing, ::Float64, ::Bool, ::Int64) at ./REPL[2]:4
 [2] top-level scope at REPL[3]:1

The part I don’t get is

  1. why doesn’t this work,
  2. why the ::Union{TX, TX},
  3. why the repeated TX type parameter.

Is this a bug? Using 1.4-rc1, but could reproduce on 1.3.

Also, modifying the converting constructor as

function Foo(Δ::T1, x::AbstractVector{T2}, fx, residual_norm::T3, converged,
             iterations) where {T1 <: Real, T2 <: Real, T3 <: Real}
    T = promote_type(T1, T2, T3)
    Tx = T.(x)
    Foo{T,typeof(Tx)}(T(Δ), Tx, fx, T(residual_norm), converged, iterations)
end

gives

julia> Foo(1, [1.0, 2.0], nothing, 1.0, true, 0)
ERROR: MethodError: no method matching Foo{Float64,Array{Float64,1},TFX} where TFX(::Float64, ::Array{Float64,1}, ::Nothing, ::Float64, ::Bool, ::Int64)
Stacktrace:
 [1] Foo(::Int64, ::Array{Float64,1}, ::Nothing, ::Float64, ::Bool, ::Int64) at ./REPL[8]:5
 [2] top-level scope at REPL[9]:1

where what follows where is really unusual.

If you constrain T in the struct to also be <:Real it works. I am guessing your constraints in the outer constructors are stronger in some sense than the default constructor since they enforce <:Real but weaker since they don’t enforce TX<:AbstractVector{T} which seems like an ambiguity.

julia> methods(Foo)
# 2 methods for type constructor:
[1] (::Type{Foo})(Δ::T, x::TX, fx::TFX, residual_norm::T, converged::Bool, iterations::Int64) where {T, TX<:AbstractArray{T,1}, TFX} in Main at REPL[2]:2
[2] (::Type{Foo})(Δ::T1, x::AbstractArray{T2,1}, fx, residual_norm::T3, converged, iterations) where {T1<:Real, T2<:Real, T3<:Real} in Main at REPL[4]:3

do you think that the

should be reported as an issue? also the Union{TX,TX} which I think is related.

Yeah, the second where error message looks weird to me.

Thanks, reported here:

https://github.com/JuliaLang/julia/issues/34824