Skip to content

Conversation

@aviatesk
Copy link
Member

@aviatesk aviatesk commented Jul 29, 2025

This addresses type inference issues with filtered list comprehensions reported in #59111. The root cause is the compiler's inability to perform type refinement in cases like:

let t::Tuple{Any,Any}
    x, y = t
    if x isa Int
        return t # still `::Tuple{Any,Any}`
    end
    nothing
end

Fixing this fundamentally would require extensive improvements to
abstract interpretation. As a workaround, I improved the iterators.jl
implementation to avoid this pattern.

One implementation consideration: if iterate(f.itr, state...) always
returns Union{Nothing,Tuple{Any,Any}}, the if y isa Tuple{Any,Any}
branch should be unnecessary. However, it's unclear whether we can
safely assume this for the iterate interface, so I kept the branch for
now.

@aviatesk aviatesk mentioned this pull request Jul 29, 2025
@aviatesk aviatesk force-pushed the avi/filtered-list-comprehension-inference branch from 3c99fd0 to 664a71d Compare July 29, 2025 16:57
Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, it is permitted for any two-element struct to be returned (such as NamedTuple or Pair), as long as it implements indexing corresponding to fields

@aviatesk
Copy link
Member Author

AFAIK, it is permitted for any two-element struct to be returned (such as NamedTuple or Pair), as long as it implements indexing corresponding to fields

Okay, then I guess we probably need to update the docstring for iterate (https://docs.julialang.org/en/v1.13-dev/base/collections/#Base.iterate) too..

@adienes
Copy link
Member

adienes commented Jul 29, 2025

whether or not the inner iterator uses or is allowed to use a Pair or a Tuple, why does Filter have to pass that choice along?

@martinholters
Copy link
Member

But even if iterate((f.itr, state...) returns some non-tuple, it should still be ok for iterate(f, state...) to always return a tuple, no?

@martinholters
Copy link
Member

Heh, within a second :)

@aviatesk
Copy link
Member Author

Indeed, from the view point of iterate(::Filter), it might not be a problem to force it to return Tuple{Any,Any}.

@Keno
Copy link
Member

Keno commented Jul 29, 2025

Actually I think the previous change may be better. When iterators don't return tuples, ther s often a reason, e.g. to avoid excessive specialization. While I agree it is legal to return the tuple, I'm not sure it's useful.

@aviatesk
Copy link
Member Author

That's certainly true. There's a possibility that other implementations of iterate are manually calling iterate(::Filter). It's a question of whether to avoid breaking such cases or to prioritize type inference results. I don't have a strong opinion on this honestly. Since Tuple{Any,Any} can cover most cases, the previous implementation might be safer and more convenient.

aviatesk added 3 commits July 30, 2025 02:37
This addresses type inference issues with filtered list comprehensions
reported in #59111. The root cause is the compiler's
inability to perform type refinement in cases like:

```julia
let t::Tuple{Any,Any}
    x, y = t
    if x isa Int
        return t # still `::Tuple{Any,Any}`
    end
    nothing
end
```

Fixing this fundamentally would require extensive improvements to
abstract interpretation. As a workaround, I improved the iterators.jl
implementation to avoid this pattern.

One implementation consideration: if `iterate(f.itr, state...)` always
returns `Union{Nothing,Tuple{Any,Any}}`, the `if y isa Tuple{Any,Any}`
branch should be unnecessary. However, it's unclear whether we can
safely assume this for the iterate interface, so I kept the branch for
now.
@aviatesk aviatesk force-pushed the avi/filtered-list-comprehension-inference branch from a8eade2 to 45e6823 Compare July 29, 2025 17:37
@nsajko nsajko added the iteration Involves iteration or the iteration protocol label Jul 30, 2025
@nsajko
Copy link
Member

nsajko commented Jul 30, 2025

Is there an actual example of an iterator that returns Union{Nothing, Collection} where !(Collection <: Tuple{Any,Any})?

@aviatesk
Copy link
Member Author

There are lots of cases. You can easily write such a custom iterate implementation while satisfying the iterator protocol.

@aviatesk aviatesk merged commit c230f9f into master Jul 30, 2025
6 of 8 checks passed
@aviatesk aviatesk deleted the avi/filtered-list-comprehension-inference branch July 30, 2025 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

iteration Involves iteration or the iteration protocol

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants