Description
This is good:
julia> f(x::Tuple{Any,Vararg}) = x[1] + f(x[2:end])
f (generic function with 1 method)
julia> f(x::Tuple{}) = 0
f (generic function with 2 methods)
julia> only(Base.return_types(f, Tuple{Tuple{Int,Int,Int,Int,Int,Int,Int}}))
Int64
However, when introducing a level of indirection, inference widens more aggressively when detecting the recursion and fails:
julia> g(x::Tuple{Any,Vararg}) = x[1] + _g(x[2:end])
g (generic function with 1 method)
julia> g(x::Tuple{}) = 0
g (generic function with 2 methods)
julia> _g(x) = g(x)
_g (generic function with 1 method)
julia> only(Base.return_types(g, Tuple{Tuple{Int,Int,Int,Int,Int,Int,Int}}))
Any
This is particularly unfortunate as this kind of indirection is implicitly introduced when adding kwargs:
julia> h(x::Tuple{Any,Vararg}; kwargs...) = x[1] + h(x[2:end]; kwargs...)
h (generic function with 1 method)
julia> h(x::Tuple{}; kwargs...) = 0
h (generic function with 2 methods)
julia> only(Base.return_types(h, Tuple{Tuple{Int,Int,Int,Int,Int,Int,Int}}))
Any
A manifestation of this effect is #45715.
We deliberately distinguish the cases of direct vs. indirect recursion:
julia/base/compiler/abstractinterpretation.jl
Lines 538 to 546 in cda99c8
I'd argue that we shouldn't do this and instead, treat both cases equal. As a first trial balloon, I did
--- a/base/compiler/abstractinterpretation.jl
+++ b/base/compiler/abstractinterpretation.jl
@@ -542,7 +542,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
l_comparison = length((unwrap_unionall(comparison)::DataType).parameters)
spec_len = max(spec_len, l_comparison)
else
- comparison = method.sig
+ comparison = topmost.linfo.specTypes
end
if isdefined(method, :recursion_relation)
That indeed lets above cases all be inferred as Int
(and also fixes #45715). However, one @inferred
test in the broadcast tests then fails. Although I didn't really know what I was doing there, I was not expecting this to worsen inference results, but maybe it's now triggering some other edge case? Anyway, feedback from @vtjnash on why these two cases are treated differently and whether it's a good idea to try to unify them would be welcome.