-
-
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
Add promote_typejoin() and use it instead of typejoin() to preserve Union{T, Nothing/Missing} #25553
Conversation
6d9edf6
to
bfcd16d
Compare
test/tuple.jl
Outdated
@test x isa Vector{Tuple{Int,Union{Int,T}}} | ||
|
||
y = map(v -> (v[1], v[2]), [(1, T()), (1, 2)]) | ||
@test y isa Vector{Tuple{Int64,Union{T,Int64}}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These Int64
explain the 32-bit CI failures. Will fix in the next push.
bfcd16d
to
8d90c23
Compare
Very nearly a CI straight flush. Win32 failure was a timeout. |
😎 |
base/inference.jl
Outdated
@@ -1873,7 +1873,7 @@ function limit_tuple_type_n(@nospecialize(t), lim::Int) | |||
p = t.parameters | |||
n = length(p) | |||
if n > lim | |||
tail = reduce(typejoin, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) | |||
tail = reduce(promote_join, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be changed in inference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. I thought this was needed to change the promotion of tuples, but clearly I've been overzealous. I've reverted it.
Seems basically right. |
…, Void/Missing} This allows for a more efficient code and array layout than using Any.
…priate typejoin() is now used only for inference, and promote_join() everywhere else to choose the appropriate element type for a collection.
…or Nothing Reuse the typejoin() logic.
8d90c23
to
39ceccb
Compare
base/tuple.jl
Outdated
end | ||
return r | ||
end | ||
|
||
# promotion | ||
|
||
promote_rule(::Type{S}, ::Type{T}) where {S<:Tuple, T<:Tuple} = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be removed. Changing the type of [(1,), (1.0,)]
seems to be outside of the goal here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The goal is to have [(1,), (missing,)] isa Vector{Tuple{Union{Int,Missing}}
rather than Tuple{Any}
. I agree that's not totally required in this PR, but it makes a lot of sense to avoid an inconsistency with map(identity, [(1,), (missing,)])
. That's also consistent with [[1], [missing]]
. In general I don't see why tuples shoudn't implement promotion, like other containers.
Another solution is to use promote_join
rather than promote_type
. This could be done either here (i.e. special-case tuples promotion to just use promote_join
), or more generally when concatenating arrays. I would prefer a solution which applies consistently to all types.
Anyway I've added a commit to revert this for now, so that we can address these issues separately.
base/namedtuple.jl
Outdated
@@ -55,6 +55,12 @@ indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1) | |||
isempty(::NamedTuple{()}) = true | |||
isempty(::NamedTuple) = false | |||
|
|||
promote_rule(::Type{NamedTuple{n, S}}, ::Type{NamedTuple{n, T}}) where {n, S, T} = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also remove this for now. Can be raised as a separate issue if necessary.
Remove promotion tests, revert unneeded Any[ specifications. Also add @_pure_meta to two methods to preserve current behavior (without it, vcat((1,), (1.0,)) is not inferred as Vector{Tuple{Real}}, which is caught by a test which previously would not detect this problem).
Thanks for the review. I must say I'm not very happy with the name |
promote_jointype? |
base/promotion.jl
Outdated
function typejoin(@nospecialize(a), @nospecialize(b)) | ||
typejoin(@nospecialize(a), @nospecialize(b)) = (@_pure_meta; join_types(a, b, typejoin)) | ||
|
||
function join_types(@nospecialize(a), @nospecialize(b), f::Function) | ||
@_pure_meta |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not pure anymore (due to the abstractly typed f::Function
parameter)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I always get this wrong... Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need this function to be pure though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea. We could use a loop inside @eval
to define two separate functions which would be pure, without duplicating the code.
I've renamed |
🆗 |
@@ -237,7 +237,7 @@ eltype(::Type{TwicePrecision{T}}) where {T} = T | |||
|
|||
promote_rule(::Type{TwicePrecision{R}}, ::Type{TwicePrecision{S}}) where {R,S} = | |||
TwicePrecision{promote_type(R,S)} | |||
promote_rule(::Type{TwicePrecision{R}}, ::Type{S}) where {R,S} = | |||
promote_rule(::Type{TwicePrecision{R}}, ::Type{S}) where {R,S<:Number} = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is right. For some reason TwicePrecision{T}
's parameter does not need to be a Number. @timholy Why is that?
@@ -5,61 +5,66 @@ | |||
""" | |||
typejoin(T, S) | |||
|
|||
Compute a type that contains both `T` and `S`. | |||
|
|||
Return the closest common ancestor of `T` and `S`, i.e. the narrowest type from which |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this docstring could be made clearer. I'm trying to figure out exactly what typejoin
and promote_typejoin
are doing and I don't think the current descriptions are complete. The current docstring for typejoin
states that it returns
the narrowest type from which they both [T and S] inherit
but that must always be Union{T,S}
. However, my understanding is that typejoin
doesn't return unions and that promote_typejoin
was introduced exactly because of that. So what is this function returning? My guess would be something like
T
ifS<:T
S
ifT<:S
elsemin(TS)
for whichT<:TS
andS<:TS
andisabstract(TS)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I just copied "closest common ancestor" from a comment and tried to make it a little more explicit. You're right that typejoin
returns an abstract supertype and never a Union
AFAIK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the definition I stated above is correct then it might be fine to just define ancestor to cover only declared abstract types (and concrete types), i.e. exclude unions. It probably makes sense the declared subset of the type lattice is a tree so ancestor feels natural.
The name of the function would be slightly a misnomer as the lattice join is the least upper bound, but I guess some upper bound is fine for our use.
Examples would then also be helpful in the docstring. I wouldn't mind preparing the PR but I would need a confirmation that my understanding of the function is correct.
This is a continuation of #24332, which cannot be reopened. It's an alternative to adding a more general strict/exact promotion mechanism (#25423). It achieves the same goal, i.e. it ensures that we use
Union{T, Nothing/Missing}
instead ofAny
withcollect
,map
,broadcast
and withTuple
andNamedTuple
promotion.