Skip to content
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

inference: allow PartialStruct to have Union{} field to indicate strict undef #57541

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

aviatesk
Copy link
Member

@aviatesk aviatesk commented Feb 26, 2025

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 PartialStructs
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

@serenity4
Copy link
Contributor

The initial choice to make length(fields) <= length(fieldcount(typ)) was to improve performance (particularly so for large structs, but small structs also benefit from it). However, if benchmarks/build times show no noticeable regression, then the consistency is nice to have 👍

fldcnt = fieldcount_noerror(objt)::Int
fields = Any[fieldtype(objt0, i) for i = 1:fldcnt]
if fields[fldidx] === Union{}
return nothing # refine this field
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@aviatesk aviatesk force-pushed the avi/strict-undef-fields branch from c8dcd2c to 1397a8b Compare February 27, 2025 08:36
@aviatesk aviatesk changed the title wip: inference: allow PartialStruct to have Union{} field to indicate strict undef inference: allow PartialStruct to have Union{} field to indicate strict undef Feb 27, 2025
undef::BitVector # represents whether a given field may be undefined
undefs::Vector{Union{Nothing,Bool}} # represents whether a given field may be undefined
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed undef to undefs to avoid naming collision with the undef initializer.

Comment on lines 352 to 387
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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've inlined some logic implemented here because it is now only used once in other files.

@aviatesk aviatesk force-pushed the avi/strict-undef-fields branch from 1397a8b to b00838b Compare February 27, 2025 09:31
@aviatesk
Copy link
Member Author

@nanosoldier runbenchmarks("inference", vs=":master")

@nanosoldier
Copy link
Collaborator

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}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: this method is not currently used from Compiler.jl. PR #57553 aims to fix that.

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}}`
@aviatesk aviatesk force-pushed the avi/strict-undef-fields branch from e9ee6e4 to 1475330 Compare February 28, 2025 08:25
@aviatesk aviatesk added the merge me PR is reviewed. Merge when all tests are passing label Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:inference Type inference merge me PR is reviewed. Merge when all tests are passing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants