-
-
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
Extend PartialStruct
to represent non-contiguously defined fields
#57304
base: master
Are you sure you want to change the base?
Conversation
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.
Instead of a separate BitVector{Bool}
, would it be better to add a tombstone / marker for non-Const
fields (e.g. Any
)?
That seems easier to iterate and more memory-efficient for the common case of small structs.
The issue with choosing We could have a sentinel value like |
The sentinel for a value that definitely throws on access is Union{} and appears throughout inference for that purpose. If you are only tracking may-throw, then you need something like our VarInfo to add a boolean for it, but that complicates the type lattice |
I think @serenity4 wants the opposite here - something that definitely doesn't throw on access. The pattern to copy is probably That makes this two separate enhancements: (1) handling non-contiguous fields, and (2) tracking type / defined-ness independently - seems fair to me |
We need a very basic "may-throw" information here, which in the current state is encoded as a lack of information. The idea was to avoid complicating the type lattice, which is why I went for a solution that does not require introducing a new sentinel value, and instead keeps track externally of available information. But on the other hand, it makes little sense to iterate this |
Note that (2) is already implemented in current |
Is it possible to represent on current |
No, |
Hmm, it feels strange that you can represent a My vote would be to either go in the VarState direction and make definedness independent from the type information, or else keep PartialStruct's existing limitations for |
In what situation would you want to model In the current state (
The only thing it doesn't model is certainty that a field is undefined, but I don't find any situation where this would be particularly useful. (plus, as we can't unset fields, it would be hard to make this guarantee in the first place, unless it is immutable or all operations on the object are known since its creation) |
You should read this as "maybe-undefined and Const when it is defined" because the That is what |
Ah I see, indeed we have been conservative with In fact, it is already possible to do so using conditional branching information with My understanding is that since I'd suggest to keep relying on the same mechanisms to express that, and perhaps use |
The
So, right now, the Though since field defined-ness is really similar variable defined-ness information represented by Regardless of which approach we take, we'll need to carefully audit all the places where |
VarState's
I think that's the main point of discussion - the current data structure can't represent those two pieces of information separately, since That said, I'm a fan of @serenity4's plan above to leave that enhancement for later - it seems likely to impact a lot more code vs. the smaller changeset here |
I'm a bit confused about this. Are you saying we can't represent a struct PartialStruct
typ
fields::Vector{Any}
maybeundef::BitVector
end
x = PartialStruct(Base.RefValue{Any}, Any[Const(42)], trues(1)) We'd need to handle the "maybe undef" information correctly everywhere we access |
We would essentialy go with a vectorized version of
The main difference with the current state of this PR (besides In this way, we move out the information that the |
If we are worried about memory efficiency/performance (notably for large structs), we can always keep the optimization that if there exists That seems to be the cost of being able to express both |
This description sounds concerning, as it seems to mean we cannot correctly represent the tmerge of two of these such types |
We can't simply |
8a9f31d
to
67b9168
Compare
Oh, I see, that is slightly confusing, since it goes the opposite direction of the lattice normally is implemented, and so it discards information slightly faster than expected. This change to the bitvector approach (with an optimization that it only gets allocated if any are maybe-undefined) seems slightly clearer semantically. |
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.
Tests seem to be passing (besides one OOM in threading tests, not sure if that's related), should we run benchmarks to see if the current design has any performance regressions? It seemed locally that compile times had increased a fair bit, but I don't seem to observe that in CI. I would nonetheless recommend to check.
@nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. |
Co-authored-by: Shuhei Kadowaki <[email protected]>
@nanosoldier |
So far,
PartialStruct
has been unable to represent non-contiguously defined fields, where e.g. a struct would have fields 1 and 3 defined but not field 2. This PR extends it so that such information may be represented withPartialStruct
, extending the applicability of optimizations e.g. introduced in #55297 by @aviatesk or #57222.The semantics of
new
prevent the creation of a struct with non-contiguously defined fields, therefore this change is mostly relevant to model mutable structs whose fields may be previously set or assumed to be defined after creation, or immutable structs whose creation is opaque.Notably, with this change we may now infer information about structs in the following case:
whereas previously, only information gained successively with
isdefined(mut, :x) && isdefined(mut, :y) && isdefined(mut, :z)
could allow inference to modelmut
having itsz
field defined.