-
-
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
normalize unions made in subtyping #49276
Conversation
What does that union simplify to then? |
Without simplification, there may be 2 copies of |
Oh, so the normalization here moves the |
The normalization here replaces |
src/jltypes.c
Outdated
(!has_free && !jl_has_free_typevars(temp[j]) && | ||
jl_subtype(temp[i], temp[j]))) { |
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.
Perhaps we should follow the rule below (see subtype path in simple_join
)?
Although this path has been turned off
// issue #24521: don't merge Type{T} where typeof(T) varies
!(jl_is_type_type(a) && jl_is_type_type(b) && jl_typeof(jl_tparam0(a)) != jl_typeof(jl_tparam0(b)))) {
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.
Good observation. I moved that here. I wanted to be careful not to make too big of a change at once, in case something in a package was depending on it significantly doing one thing. Maybe we should try to move more of that simple_tmeet function into simple_union, but I think a couple things are heuristic based there, so maybe not all of it should be combined.
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.
but I think a couple things are heuristic based there, so maybe not all of it should be combined.
I think a basic rule is keeping the pointer identity if possible?
#49277 seems to be a good template and if we do that, I think we can move all type-specific branch into simple_union
and let simple_join
handle non-type bits along.
BTW, I didn't measure the performance influence.
But if we decided to turn this path on, then a good optimization to have is skipping all check if temp[i]
and temp[j]
come from the same part.
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 was thinking of this check mostly, since I don't know if this is accurate for a Union in general:
if (jl_is_typevar(a) && obviously_egal(b, ((jl_tvar_t*)a)->lb))
return a;
That may be a good thing to check for during jl_type_union
too to save repeated work in the case of repeatedly appending one element (e.g. Union{Union{Union{A, B}, C}, Union{D, E}}
, since we only need to check the new elements against the old ones). A starting point for that might be to alloca a second buffer that holds the index of which where union starts from to fill the buffer. I think the trickiest ones to detect are cases like this, since "obviously" it can return the first object, but since we only do a subtype check (not equality), we may conclude the result should be that of prepending A
to the second Union, and fail to realize that will give back the first Union:
Union{ Union{A,B,C}, Union{B,C} }
That suggests perhaps we should also do the same exhaustive sort of check afterwards too, as templated in #49277, to see if all the elements remaining are equivalent to an existing one. Once we merge this and #49277, I can try prototyping that additional check.
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 was thinking of this check mostly, since I don't know if this is accurate for a Union in general:
I think it's accurate for Union
, at least for those without free vars?
But since Union
doesn't normalize it for now, I think we can leave these 2 branches in simple_join
.
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.
That suggests perhaps we should also do the same exhaustive sort of check afterwards too, as templated in #49277
Hoist some egal check here seems good?
Then we just need to count how many remaining temp
comes from a
or b
.
We observed a case where simple_tmeet made a Union of egal things, which is undesirable. There also was no sorting of the result, as it normally done, and theoretically, simplification with an omit_bad_union to remove `S` could similar result in a Union that should be further simplified to remove redundancies. ``` Union{Union{Val{T}, S} where T<:AbstractString, Union{Val{T}, Int64} where T<:AbstractString} where S ``` (In principle, that simplification might also be possible to do during the original jl_type_union call when flattening it.)
7e49d0a
to
2e66297
Compare
We observed a case where simple_tmeet made a Union of egal things, which is undesirable. There also was no sorting of the result, as it normally done, and theoretically, simplification with an omit_bad_union to remove `S` could similar result in a Union that should be further simplified to remove redundancies. ``` Union{Union{Val{T}, S} where T<:AbstractString, Union{Val{T}, Int64} where T<:AbstractString} where S ``` (In principle, that simplification might also be possible to do during the original jl_type_union call when flattening it: see JuliaLang#49279)
We observed a case where simple_tmeet made a Union of egal things, which is undesirable. There also was no sorting of the result, as it normally done, and theoretically, simplification with an omit_bad_union to remove
S
could similar result in a Union that should be further simplified to remove redundancies.(In principle, that simplification might also be possible to do during the original jl_type_union call when flattening it.)
This prevents the bad inference path that occurs in #48228 from happening, without fixing the type system issue there (which can be reproduced on master by starting inference at one of the later stack frames where the complex unions are already present)