-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inference regression on master for "collector arguments" #22513
Comments
It's due to a collector argument, but the situation is a bit more complicated because the collector argument is not only used for collecting. It's a bit like the following: foo(in1::Tuple{}, in2::Tuple{}) = _foo((), in1, in2)
_foo(out, in1::Tuple{}, in2::Tuple{}) = out
_foo(out, in1::Tuple, in2::Tuple{}) = _foo((out..., Val{length(out)}()), Base.tail(in1), in2)
_foo(out, in1::Tuple{}, in2::Tuple) = _foo((out..., 0), in1, Base.tail(in2))
_foo(out, in1::Tuple, in2::Tuple) = _foo((out..., in1[1]), Base.tail(in1), Base.tail(in2)) If it wasn't for the makevals(::Tuple{}) = ()
makevals(in::Tuple) = (makevals(Base.tail(in))..., Val{length(Base.tail(in))}())
foo(in1::Tuple, in2::Tuple) = _foo(makevals(in1), in1, in2)
_foo(vals, in1::Tuple{}, in2::Tuple{}) = ()
_foo(vals, in1::Tuple, in2::Tuple{}) = (vals[1], _foo(Base.tail(vals), Base.tail(in1), in2)...)
_foo(vals, in1::Tuple{}, in2::Tuple) = (0, _foo(vals, in1, Base.tail(in2))...)
_foo(vals, in1::Tuple, in2::Tuple) = (in1[1], _foo(Base.tail(vals), Base.tail(in1), Base.tail(in2))...) However, if it wasn't a makevals(in::Tuple) = (makevals(Base.tail(in))..., Val{length(Base.tail(in))})
_foo(vals, in1::Tuple, in2::Tuple{}) = (vals[1](), _foo(Base.tail(vals), Base.tail(in1), in2)...)
# other methods as above again is not inferable, because the return type of I'm not sure how inferability and avoiding unnecessary instantiations could be achieved at the same time. Any ideas anyone? |
Here's an even simpler example, inferrably computing 2^N: twototheN(ref::Tuple{}) = ()
twototheN(ref::Tuple{Any,Vararg{Any}}) = _twototheN((true, true), Base.tail(ref))
_twototheN(out, ref::Tuple{Any,Vararg{Any}}) = _twototheN((out..., out...), Base.tail(ref))
_twototheN(out, ref::Tuple{}) = out
|
arbitrary arithmetic is not expected to be inferrable |
This isn't really arithmetic, it's just exercising the type system. I understand this is a consequence of the redesign of inference to make sure it converges. Wouldn't there be a way to tell that the second argument to |
No, during inference, the second argument could be a |
Since there's a workaround using |
If possible, I'd recommend the workaround of not using collector arguments, though: twototheN(ref::Tuple{}) = (true,)
twototheN(ref::Tuple) = (twototheN(Base.tail(ref))..., twototheN(Base.tail(ref))...) |
Inference also can't easily determine that this will result in the program terminating. For example, we could just replace the base case with: We could add heuristics to detect any arbitrary subset of the halting problem, but current experience with base has indicated that our rules are already sufficiently permissive such that there are simple rewrites (for humans, not for computers) that ensure the useful cases are inferrable while the uninferrable / barely inferrable cases can get rejected fairly quickly. |
Is this another instance of the same issue: tuple_split(t::Tuple, ::Type{Val{N}}) where {N} = _tsplit((), t, Val{N})
@inline _tsplit(t1::NTuple{N1}, t2::NTuple{N2}, ::Type{Val{N1}}) where {N1,N2} = t1, t2
@inline _tsplit(t1::NTuple{N1}, t2::NTuple{N2}, ::Type{Val{N3}}) where {N1,N2,N3}= _tsplit((t1...,t2[1]), Base.tail(t2), Val{N3})
|
Another one, permuting tuples (where tpermute(t::NTuple{N,T}, p::NTuple{N,Int}) where {T,N} = _tpermute((), t, p)
_tpermute(tdst::NTuple{N,T}, tsrc::NTuple{N,T}, p::NTuple{N,Int}) where {T,N} = tdst
_tpermute(tdst::NTuple{M,T}, tsrc::NTuple{N,T}, p::NTuple{N,Int}) where {T,M,N} = _tpermute((tdst...,tsrc[p[M+1]]), tsrc, p) On Julia 0.6, the return type of e.g. If type inference does no longer work correctly for these cases, it forces us back to using more Also strange, on neither Julia 0.6 or 0.7, the call to |
Those shouldn't be inferrable, but this is:
|
😳 to much trying lispy style tuple manipulation made me forgot about the easy solution. Thanks for that. Even without tpermute(t::NTuple{N,T}, p::NTuple{N,Int}) where {T,N} = _tpermute(t, p)
_tpermute(t, p::Tuple{}) = ()
_tpermute(t, p::Tuple) = (t[p[1]], _tpermute(t, tail(p))...) which also is inferred correctly. But than again, for my overly complicated implementation of above, it's actually not the final tuple length that is incorrectly inferred, but the type of elements, as reported in #23278. I noticed you have already started working on that, so thanks for that. |
Regarding #22513 (comment), this should work: tuple_split(t::Tuple{Vararg{Any,N}}, ::Type{Val{N}}) where {N} = (t, ())
function tuple_split(t::Tuple, ::Type{Val{N}}) where {N}
t1, t2 = tuple_split(Base.front(t), Val{N})
return (t1, (t2..., last(t)))
end |
This shows up as nightly test failures for AxisArrays.jl which were not present on 0.6.
Here is the simplest example:
JuliaArrays/AxisArrays.jl#92
The text was updated successfully, but these errors were encountered: