From e5b13634fa5d1f827f19ace8e7192bac8df87455 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 18 Mar 2026 13:53:57 +0100 Subject: [PATCH 1/2] ABI export: export `Nothing`/`Cvoid` as null This patch changes the export of `Nothing`/`Cvoid` to JSON `null`. Methods with `Cvoid` return type will be exported with `"returns": null` and `Ptr{Cvoid}`, as return type or argument type, will be exported with `{"kind": "pointer", pointee_type_id": null}`. Co-Authored-By: Claude Sonnet 4.6 --- src/abi_export.jl | 13 ++++++++++--- test/cli.jl | 24 ++++++++++++++++++++++++ test/libsimple.jl | 4 ++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/abi_export.jl b/src/abi_export.jl index c62e36f..8b0e8d7 100644 --- a/src/abi_export.jl +++ b/src/abi_export.jl @@ -10,6 +10,7 @@ function recursively_add_types!(types::Base.IdSet{DataType}, @nospecialize(T::Da while T.name === Ptr.body.name push!(types, T) T = T.parameters[1] # unwrap Ptr{...} + T === Nothing && return types T in types && return types end if T.name.module === Core && T ∉ C_friendly_types @@ -68,7 +69,8 @@ function field_name_json(@nospecialize(dt::DataType), field::Int) end function emit_pointer_info!(ctx::TypeEmitter, @nospecialize(dt::DataType); indent::Int = 0) - pointee_type_id = ctx.type_ids[dt.parameters[1]] + pointee = dt.parameters[1] + pointee_type_id = pointee === Nothing ? "null" : string(ctx.type_ids[pointee]) let indented_println(args...) = println(ctx.io, " " ^ indent, args...) indented_println("{") indented_println(" \"id\": ", ctx.type_ids[dt], ",") @@ -167,7 +169,12 @@ function emit_method_info!(ctx::TypeEmitter, method::Core.Method; indent::Int = println(ctx.io, i == length(sig.parameters) ? " }" : " },") end indented_println(" ],") - indented_println(" \"returns\": { \"type_id\": ", ctx.type_ids[rt], " }") + rt_json = if rt === Nothing + "null" + else + string("{ \"type_id\": ", ctx.type_ids[rt], " }") + end + indented_println(" \"returns\": ", rt_json) print(ctx.io, " " ^ indent, "}") end end @@ -211,7 +218,7 @@ function write_abi_metadata(io::IO) for T in sig.parameters[2:end] recursively_add_types!(types, T) end - recursively_add_types!(types, rt) + rt !== Nothing && recursively_add_types!(types, rt) end end diff --git a/test/cli.jl b/test/cli.jl index ce126c1..6076179 100644 --- a/test/cli.jl +++ b/test/cli.jl @@ -113,6 +113,30 @@ end # `Ptr{CTree{Float64}}` should refer (recursively) back to the original type id Ptr_CTree_Float64 = abi["types"][CVector_CTree_Float64["fields"][2]["type_id"]] @test Ptr_CTree_Float64["pointee_type_id"] == CTree_Float64_id + + # `return_void(x::Ptr{Cvoid}, y::Ptr{Ptr{Cvoid}})::Cvoid` + fn_void = abi["functions"][findfirst(f -> f["symbol"] == "return_void", abi["functions"])::Int] + @test fn_void["returns"] === nothing + # `Ptr{Cvoid}` + ptr_cvoid = abi["types"][findfirst(t -> t["id"] == fn_void["arguments"][1]["type_id"], abi["types"])::Int] + @test ptr_cvoid["kind"] == "pointer" + @test ptr_cvoid["pointee_type_id"] === nothing + # `Ptr{Ptr{Cvoid}}` + ptr_ptr_cvoid = abi["types"][findfirst(t -> t["id"] == fn_void["arguments"][2]["type_id"], abi["types"])::Int] + @test ptr_ptr_cvoid["kind"] == "pointer" + @test ptr_ptr_cvoid["pointee_type_id"] == ptr_cvoid["id"] + + # `return_void_ptr()::Ptr{Cvoid}` + fn_void_ptr = abi["functions"][findfirst(f -> f["symbol"] == "return_void_ptr", abi["functions"])::Int] + ret_ptr = abi["types"][findfirst(t -> t["id"] == fn_void_ptr["returns"]["type_id"], abi["types"])::Int] + @test ret_ptr["kind"] == "pointer" + @test ret_ptr["pointee_type_id"] === nothing + + # `return_void_ptr_ptr()::Ptr{Ptr{Cvoid}}` + fn_void_ptr_ptr = abi["functions"][findfirst(f -> f["symbol"] == "return_void_ptr_ptr", abi["functions"])::Int] + ret_ptr_ptr = abi["types"][findfirst(t -> t["id"] == fn_void_ptr_ptr["returns"]["type_id"], abi["types"])::Int] + @test ret_ptr_ptr["kind"] == "pointer" + @test ret_ptr_ptr["pointee_type_id"] == ret_ptr["id"] end @testset "CLI library privatize end-to-end" begin diff --git a/test/libsimple.jl b/test/libsimple.jl index 64126ff..0229b36 100644 --- a/test/libsimple.jl +++ b/test/libsimple.jl @@ -45,6 +45,10 @@ Base.@ccallable function countsame(list::Ptr{MyTwoVec}, n::Int32)::Int32 return count end +Base.@ccallable return_void(x::Ptr{Cvoid}, y::Ptr{Ptr{Cvoid}})::Cvoid = nothing +Base.@ccallable return_void_ptr()::Ptr{Cvoid} = Ptr{Cvoid}() +Base.@ccallable return_void_ptr_ptr()::Ptr{Ptr{Cvoid}} = Ptr{Ptr{Cvoid}}() + export countsame, copyto_and_sum # FIXME? varargs From fd5a2856a4b2917888c521d2bd9df7cc64995acd Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 27 Mar 2026 14:12:16 +0100 Subject: [PATCH 2/2] ABI export: return type uses {type_id: null} for Nothing Co-Authored-By: Claude Sonnet 4.6 --- src/abi_export.jl | 8 ++------ test/cli.jl | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/abi_export.jl b/src/abi_export.jl index 8b0e8d7..ed6132e 100644 --- a/src/abi_export.jl +++ b/src/abi_export.jl @@ -169,12 +169,8 @@ function emit_method_info!(ctx::TypeEmitter, method::Core.Method; indent::Int = println(ctx.io, i == length(sig.parameters) ? " }" : " },") end indented_println(" ],") - rt_json = if rt === Nothing - "null" - else - string("{ \"type_id\": ", ctx.type_ids[rt], " }") - end - indented_println(" \"returns\": ", rt_json) + rt_type_id = rt === Nothing ? "null" : string(ctx.type_ids[rt]) + indented_println(" \"returns\": { \"type_id\": ", rt_type_id, " }") print(ctx.io, " " ^ indent, "}") end end diff --git a/test/cli.jl b/test/cli.jl index 6076179..bb199c8 100644 --- a/test/cli.jl +++ b/test/cli.jl @@ -116,7 +116,7 @@ end # `return_void(x::Ptr{Cvoid}, y::Ptr{Ptr{Cvoid}})::Cvoid` fn_void = abi["functions"][findfirst(f -> f["symbol"] == "return_void", abi["functions"])::Int] - @test fn_void["returns"] === nothing + @test fn_void["returns"]["type_id"] === nothing # `Ptr{Cvoid}` ptr_cvoid = abi["types"][findfirst(t -> t["id"] == fn_void["arguments"][1]["type_id"], abi["types"])::Int] @test ptr_cvoid["kind"] == "pointer"