Interesting. I can reproduce.
Also worth pointing out that just like changing foo
to treat B
and C
the same causes type stability, replacing the c
in the function call with b
is also type stable with the original foo.
julia> @code_warntype foo(A{Float64,0}, b, c, b, b)
Variables:
t<optimized out>
ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 1)::C{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
Core.SSAValue(5) = (Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 3)::B{Float64,2}
# meta: location REPL[4] foo 1
Core.SSAValue(9) = $(Expr(:invoke, MethodInstance for foo(::Type{A{Float64,2}}, ::B{Float64,2}, ::B{Float64,2}, ::Vararg{B{Float64,2},N} where N), :(Main.foo), A{Float64,2}, Core.SSAValue(4), Core.SSAValue(5)))::Type{A{Float64,_1}} where _1
# meta: pop location
return Core.SSAValue(9)
end::Type{A{Float64,_1}} where _1
julia> @code_warntype foo(A{Float64,0}, b, b, b, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 3)::B{Float64,2}
return A{Float64,0}
end::Type{A{Float64,0}}
Curiously, switching the position of c to anywhere but 2nd was type stable:
julia> @code_warntype foo(A{Float64,0}, c, b, b, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},B{Float64,2}}, 3)::B{Float64,2}
return A{Float64,2}
end::Type{A{Float64,2}}
julia> @code_warntype foo(A{Float64,0}, b, c, b, b)
Variables:
t<optimized out>
ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 1)::C{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
Core.SSAValue(5) = (Core.getfield)(ts::Tuple{C{Float64,2},B{Float64,2},B{Float64,2}}, 3)::B{Float64,2}
# meta: location REPL[4] foo 1
Core.SSAValue(9) = $(Expr(:invoke, MethodInstance for foo(::Type{A{Float64,2}}, ::B{Float64,2}, ::B{Float64,2}, ::Vararg{B{Float64,2},N} where N), :(Main.foo), A{Float64,2}, Core.SSAValue(4), Core.SSAValue(5)))::Type{A{Float64,_1}} where _1
# meta: pop location
return Core.SSAValue(9)
end::Type{A{Float64,_1}} where _1
julia> @code_warntype foo(A{Float64,0}, b, b, c, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},C{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},C{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},C{Float64,2},B{Float64,2}}, 2)::C{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},C{Float64,2},B{Float64,2}}, 3)::B{Float64,2}
return A{Float64,2}
end::Type{A{Float64,2}}
julia> @code_warntype foo(A{Float64,0}, b, b, b, c)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2}}, 2)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2}}, 3)::C{Float64,2}
return A{Float64,2}
end::Type{A{Float64,2}}
I’ve encountered a problem before where incrementally calling parametric functions made them all type stable, but when jumping ahead they are not. However, I started over in a new session and called them in a different order, and that doesn’t seem to be going on here.
I once again got that specifically the order b, c, b, b
is not inferable, but c
in the other three locations is.
Throwing extra b
s on the end makes it switch back and fourth:
julia> @code_warntype foo(A{Float64,0}, b, b, b, c, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2}}, 3)::C{Float64,2}
Core.SSAValue(6) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2}}, 4)::B{Float64,2}
return A{Float64,2}
end::Type{A{Float64,2}}
julia> @code_warntype foo(A{Float64,0}, b, b, b, c, b, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}, 3)::C{Float64,2}
Core.SSAValue(6) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}, 4)::B{Float64,2}
Core.SSAValue(7) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2}}, 5)::B{Float64,2}
return A{Float64,2}
end::Type{A{Float64,_1}} where _1
julia> @code_warntype foo(A{Float64,0}, b, b, b, c, b, b, b)
Variables:
t<optimized out>
ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}
Body:
begin
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 1)::B{Float64,2}
Core.SSAValue(4) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 2)::B{Float64,2}
Core.SSAValue(5) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 3)::C{Float64,2}
Core.SSAValue(6) = (Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 4)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 5)::B{Float64,2}
(Core.getfield)(ts::Tuple{B{Float64,2},B{Float64,2},C{Float64,2},B{Float64,2},B{Float64,2},B{Float64,2}}, 6)::B{Float64,2}
return A{Float64,2}
end::Type{A{Float64,2}}
I have no idea what is going on.