Extend `PartialStruct` to represent non-contiguously defined fields (#57304)
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 with `PartialStruct`, 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:
```julia
mutable struct A; x; y; z; A() = new(); end
function f()
mut = A()
# some opaque call preventing optimizations
# who knows, maybe `identity` will set fields from `mut` in a future world age!
invokelatest(identity, mut)
isdefined(mut, :z) && isdefined(mut, :x) || return
isdefined(mut, :x) & isdefined(mut, :z) # this now infers as `true`
isdefined(mut, :y) # this does not
end
```
whereas previously, only information gained successively with
`isdefined(mut, :x) && isdefined(mut, :y) && isdefined(mut, :z)` could
allow inference to model `mut` having its `z` field defined.
---------
Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com>
Co-authored-by: Shuhei Kadowaki <aviatesk@gmail.com>