inference: allow PartialStruct to represent strict undef field#57541
inference: allow PartialStruct to represent strict undef field#57541
PartialStruct to represent strict undef field#57541Conversation
245049e to
c8dcd2c
Compare
|
The initial choice to make |
| fldcnt = fieldcount_noerror(objt)::Int | ||
| fields = Any[fieldtype(objt0, i) for i = 1:fldcnt] | ||
| if fields[fldidx] === Union{} | ||
| return nothing # refine this field |
There was a problem hiding this comment.
I'm not sure I understand the comment here, doesn't refining mean returning a PartialStruct? (though it makes sense to return nothing as a Union{} field type already implies undef[fldidx] === true)
There was a problem hiding this comment.
This PR wasn't finished yesterday, so the logic might have been messed up. Undef the refined definition of PartialStruct, fields[fldidx] === Union{} is a special case, meaning that field is guaranteed not to be initialized, so we need to bail out here.
c8dcd2c to
1397a8b
Compare
PartialStruct to have Union{} field to indicate strict undefPartialStruct to have Union{} field to indicate strict undef
| struct PartialStruct | ||
| typ | ||
| undef::BitVector # represents whether a given field may be undefined | ||
| undefs::Vector{Union{Nothing,Bool}} # represents whether a given field may be undefined |
There was a problem hiding this comment.
Renamed undef to undefs to avoid naming collision with the undef initializer.
| function refines_definedness_information(pstruct::PartialStruct) | ||
| nflds = length(pstruct.undef) | ||
| something(findfirst(pstruct.undef), nflds + 1) - 1 > datatype_min_ninitialized(pstruct.typ) | ||
| end | ||
|
|
||
| function define_field(pstruct::PartialStruct, fi::Int) | ||
| if !is_field_maybe_undef(pstruct, fi) | ||
| # no new information to be gained | ||
| return nothing | ||
| end | ||
|
|
||
| new = expand_partialstruct(pstruct, fi) | ||
| if new === nothing | ||
| new = PartialStruct(fallback_lattice, pstruct.typ, copy(pstruct.undef), copy(pstruct.fields)) | ||
| end | ||
| new.undef[fi] = false | ||
| return new | ||
| end | ||
|
|
||
| function expand_partialstruct(pstruct::PartialStruct, until::Int) | ||
| n = length(pstruct.undef) | ||
| until ≤ n && return nothing | ||
|
|
||
| undef = partialstruct_init_undef(pstruct.typ, until; all_defined = false) | ||
| for i in 1:n | ||
| undef[i] &= pstruct.undef[i] | ||
| end | ||
| nf = length(pstruct.fields) | ||
| typ = pstruct.typ | ||
| fields = Any[i ≤ nf ? pstruct.fields[i] : fieldtype(typ, i) for i in 1:until] | ||
| return PartialStruct(fallback_lattice, typ, undef, fields) | ||
| end |
There was a problem hiding this comment.
I've inlined some logic implemented here because it is now only used once in other files.
1397a8b to
b00838b
Compare
|
@nanosoldier |
|
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. |
|
|
||
| function Base.getproperty(pstruct::Core.PartialStruct, name::Symbol) | ||
| name === :undef && return getfield(pstruct, :undef)::BitVector | ||
| name === :undefs && return getfield(pstruct, :undefs)::Vector{Union{Nothing,Bool}} |
There was a problem hiding this comment.
NB: this method is not currently used from Compiler.jl. PR #57553 aims to fix that.
e9ee6e4 to
1475330
Compare
|
Is this broken on 32-bit platforms? |
| - `undefs[i] === false` indicates the corresponding element in `fields` is guaranteed to be defined | ||
| - `undefs[i] === true` indicates the corresponding element in `fields` is guaranteed to be undefined | ||
| If `field[i]` is of type `Union{}`, it means the `i`-th field is never be initialized and | ||
| thus never be defined. In this case, `undefs[i]` should always be `true`. |
There was a problem hiding this comment.
Just for my understanding, was there any situation that we couldn't represent with the old encoding?
The docs seem to suggest undefs[i] === true <-> fields[i] === Union{} but maybe there's a corner-case where that's not true?
There was a problem hiding this comment.
undefs[i] === true -> fields[i] === Union{} is true, but the reverse isn't. Immutable structs with uninitialized pointer fields can have undefs[i] === true but at the same time fields[i] can be arbitrary types. We can probably make those fields[i] to Union{}, but I wanted to avoid encoding field defined-ness information into fields.
There was a problem hiding this comment.
OK, that makes sense. Thanks for the details 👍
BTW, are you required to include anything that might come out of the type, or just what went into it? For example, if I make PartialStruct(Union{Foo,Bar}, undefs=[...], fields=[...]) where:
struct Foo
a::Int
b::Int
Foo() = new()
end
struct Bar
a::Any
b::Any
Bar() = new()
endwith rand(Bool) ? Foo() : Bar() (i.e. both fields undefined) - are the types in fields required to include Int, since that describes what we might read out of the isbits fields?
Or is it still correct to leave typ at Union{} in that case?
There was a problem hiding this comment.
At least for now, PartialStruct cannot represent objects that are inferred as Union types. The expression (x::PartialStruct).typ is actually either a DataType or a UnionAll.
There was a problem hiding this comment.
The UnionAll equivalent would be PartialStruct(Foo, ...) where:
struct Foo{T}
a::T
b::T
Foo{T}() where T = new{T}()
endFoo{Int} is isbits and Foo{Any} is not
There was a problem hiding this comment.
In such cases, PartialStruct won't be created.
More precisely, in compilers that don't use MustAlias, PartialStruct.typ::DataType is always true. I remember a compiler with MustAlias sometimes creates PartialStruct with UnionAll at some point, but I think that only happens within JET. It might be safer to restrict PartialStruct.typ to DataType for now.
PartialStruct to have Union{} field to indicate strict undefPartialStruct to represent strict undef field
1545900 to
87fc6cf
Compare
This change allows `PartialStruct` to represent structs with strictly
uninitialized fields.
Now the previous `undef::BitVector` field is changed to
`undefs::Vector{Union{Nothing,Bool}}` to encode defined-ness information
of each field.
Also, this lets us fix the length of `typ::PartialStruct`'s `fields` to
always match the number of fields in `typ.typ`. Instead of the current
design where the length of `fields` changes depending on the number of
initialized fields, it seems simpler to have `PartialStruct`s
representing the same `typ` always have the same `fields` length.
So, I've included that refactoring as well.
- fixes the newly detected error in Oscar.jl
make `undefs::Vector{Union{Nothing,Bool}}`
87fc6cf to
d10579b
Compare
| for i = 1:length(fields) | ||
| @assert fields[i] !== Union{} # TODO remove me once we start to exploit strict undef-ness of fields | ||
| if fields[i] === Union{} | ||
| @assert undefs[i] === true "`Union{}` typed field should be strictly undefined" |
There was a problem hiding this comment.
The Oscar.jl tests seem to be running into this assertion now:
From worker 4: Internal error: during type inference of
From worker 4: (::Type{Oscar.IntersectionTheory.AbstractVarietyMap{V1, V2} where V2<:Oscar.IntersectionTheory.AbstractVarietyT where V1<:Oscar.IntersectionTheory.AbstractVarietyT})(Oscar.IntersectionTheory.AbstractVariety, Oscar.IntersectionTheory.AbstractVariety, Array{Oscar.MPolyQuoRingElem{Oscar.MPolyDecRingElem{Nemo.QQFieldElem, Nemo.QQMPolyRingElem}}, 1}, AbstractAlgebra.Generic.FunctionalMap{Oscar.MPolyQuoRing{Oscar.MPolyDecRingElem{Nemo.QQFieldElem, Nemo.QQMPolyRingElem}}, Oscar.MPolyDecRing{Nemo.QQFieldElem, Nemo.QQMPolyRing}})
From worker 4: Encountered unexpected error in runtime:
From worker 4: AssertionError(msg="`Union{}` typed field should be strictly undefined")
From worker 4: PartialStruct at ./coreir.jl:62
https://github.com/oscar-system/Oscar.jl/actions/runs/13611555768/job/38059503585#step:11:677
…iaLang#57541) This change allows `PartialStruct` to represent structs with strictly uninitialized fields. Now the previous `undef::BitVector` field is changed to `undefs::Vector{Union{Nothing,Bool}}` to encode defined-ness information of each field. Also, this lets us fix the length of `typ::PartialStruct`'s `fields` to always match the number of fields in `typ.typ`. Instead of the current design where the length of `fields` changes depending on the number of initialized fields, it seems simpler to have `PartialStruct`s representing the same `typ` always have the same `fields` length. So, I've included that refactoring as well. - fixes the newly detected error in Oscar.jl
This change allows
PartialStructto represent structs with strictlyuninitialized fields.
Now the previous
undef::BitVectorfield is changed toundefs::Vector{Union{Nothing,Bool}}to encode defined-ness informationof each field.
Also, this lets us fix the length of
typ::PartialStruct'sfieldstoalways match the number of fields in
typ.typ. Instead of the currentdesign where the length of
fieldschanges depending on the number ofinitialized fields, it seems simpler to have
PartialStructsrepresenting the same
typalways have the samefieldslength.So, I've included that refactoring as well.