Skip to content

Constructors for structs with unions are incorrect #541

@christiangnrd

Description

@christiangnrd

#540 should fix the crash, but the constructors are not valid.

With PR #540 checked out (and the second struct commented out), the constructor takes no arguments:

julia> using Clang.Generators

julia> options = Dict("codegen" => Dict{String, Any}("add_record_constructors" => true))
Dict{String, Dict{String, Any}} with 1 entry:
  "codegen" => Dict("add_record_constructors"=>true)

julia> ctx = create_context(joinpath(@__DIR__, "test/include/constructors.h"),
                                get_default_args(), options)
Info: Parsing headers...
Context(...)

julia> build!(ctx)
[ Info: Processing header: /Users/christian/.julia/dev/Clang/test/include/constructors.h
[ Info: Building the DAG...
┌ Warning: default libname: ":libxxx" is being used, did you forget to set `library_name` in the toml file? It's safe to ignore this warning if you are using `library_names` as an exhaustive list (if so, set `library_name` to a dummy value to supress it).
└ @ Clang.Generators ~/.julia/dev/Clang/src/generator/audit.jl:16
[ Info: Emit Julia expressions...
struct _PackedFloat3
    data::NTuple{12, UInt8}
end

function Base.getproperty(x::Ptr{_PackedFloat3}, f::Symbol)
    f === :x && return Ptr{Cfloat}(x + 0)
    f === :y && return Ptr{Cfloat}(x + 4)
    f === :z && return Ptr{Cfloat}(x + 8)
    f === :elements && return Ptr{NTuple{3, Cfloat}}(x + 0)
    return getfield(x, f)
end

function Base.getproperty(x::_PackedFloat3, f::Symbol)
    r = Ref{_PackedFloat3}(x)
    ptr = Base.unsafe_convert(Ptr{_PackedFloat3}, r)
    fptr = getproperty(ptr, f)
    GC.@preserve r unsafe_load(fptr)
end

function Base.setproperty!(x::Ptr{_PackedFloat3}, f::Symbol, v)
    unsafe_store!(getproperty(x, f), v)
end

function _PackedFloat3()
    ref = Ref{_PackedFloat3}()
    ptr = Base.unsafe_convert(Ptr{_PackedFloat3}, ref)
    ref[]
end

const PackedFloat3 = _PackedFloat3

[ Info: Done!
Context(...)

With PR #540 checked out, the second struct commented out, and line 438 of codegen.jl uncommented, the constructor takes all arguments:

julia> using Clang.Generators
Precompiling Clang...
  1 dependency successfully precompiled in 3 seconds. 28 already precompiled.
o
julia> options = Dict("codegen" => Dict{String, Any}("add_record_constructors" => true))
Dict{String, Dict{String, Any}} with 1 entry:
  "codegen" => Dict("add_record_constructors"=>true)

julia> ctx = create_context(joinpath(@__DIR__, "test/include/constructors.h"),
                                get_default_args(), options)
[ Info: Parsing headers...
Context(...)

julia> build!(ctx)
[ Info: Processing header: /Users/christian/.julia/dev/Clang/test/include/constructors.h
[ Info: Building the DAG...
┌ Warning: default libname: ":libxxx" is being used, did you forget to set `library_name` in the toml file? It's safe to ignore this warning if you are using `library_names` as an exhaustive list (if so, set `library_name` to a dummy value to supress it).
└ @ Clang.Generators ~/.julia/dev/Clang/src/generator/audit.jl:16
[ Info: Emit Julia expressions...
struct _PackedFloat3
    data::NTuple{12, UInt8}
end

function Base.getproperty(x::Ptr{_PackedFloat3}, f::Symbol)
    f === :x && return Ptr{Cfloat}(x + 0)
    f === :y && return Ptr{Cfloat}(x + 4)
    f === :z && return Ptr{Cfloat}(x + 8)
    f === :elements && return Ptr{NTuple{3, Cfloat}}(x + 0)
    return getfield(x, f)
end

function Base.getproperty(x::_PackedFloat3, f::Symbol)
    r = Ref{_PackedFloat3}(x)
    ptr = Base.unsafe_convert(Ptr{_PackedFloat3}, r)
    fptr = getproperty(ptr, f)
    GC.@preserve r unsafe_load(fptr)
end

function Base.setproperty!(x::Ptr{_PackedFloat3}, f::Symbol, v)
    unsafe_store!(getproperty(x, f), v)
end

function _PackedFloat3(x::Cfloat, y::Cfloat, z::Cfloat, elements::NTuple{3, Cfloat})
    ref = Ref{_PackedFloat3}()
    ptr = Base.unsafe_convert(Ptr{_PackedFloat3}, ref)
    ptr.x = x
    ptr.y = y
    ptr.z = z
    ptr.elements = elements
    ref[]
end

const PackedFloat3 = _PackedFloat3

[ Info: Done!
Context(...)

Should we try to generate all possible constructors by checking which fields don't overlap or should we disable constructors for these cases? For example, the struct above would generate a constructor with signature _PackedFloat3(x::Cfloat, y::Cfloat, z::Cfloat), and another with signature _PackedFloat3(elements::NTuple{3, Cfloat}). I can see this getting out of hand for more complicated unions though.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions