Optional Type Support in Kotlin/Native Generator#3
Open
RandVid wants to merge 7 commits into
Open
Conversation
added 3 commits
June 18, 2026 13:36
…d type safety and clarity - Replace `String`-based type mapping with `KotlinType` enum for consistency across primitive and reference types. - Update `swiftTypeToKotlin` function to return `KotlinType?` and adjust type mapping logic accordingly. - Introduce `KotlinType.swift` to define the `KotlinType` enum, associated cases, and descriptions. - Refactor call argument handling to leverage `KotlinType` cases (e.g., `.string`, `.unit`). - Update code to switch on `KotlinType` enum instead of raw strings for type checks. - Ensure proper handling of `String` and other specific types with the new enum.
…ative
- Extend Kotlin/Native generator to handle `[UInt8]` as `UByteArray`, with automatic parameter pinning.
- Introduce `usePinned { }` blocks for safe array memory management during native calls.
- Update generator to support nested `usePinned` for multiple `UByteArray` parameters.
- Add integration tests and demo examples to validate correct handling of `[UInt8]` parameters with various return types (`Unit`, `Int`, `String`, etc.).
- Enhance `KotlinType` mapping to include `UByteArray`.
- Extend unit tests for Kotlin/Native interop with `[UInt8]`.
- Update Kotlin/Native generator to handle `[UInt8]` return types with a KN-specific ABI. - Enhance handling of heap-allocated pointers (`uint8_t*`) and proper memory management using `free()`. - Add new demo functions for `[UInt8]` return and mixed `[UInt8]` parameters and returns. - Extend integration and unit tests to validate `[UInt8]` interop in cinterop, thunks, and wrappers. - Refactor generator logic to streamline array handling and support cases like `[UInt8]` return lengths.
mMaxy
reviewed
Jun 18, 2026
| @@ -0,0 +1,143 @@ | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This source file is part of the Swift.org open source project | |||
added 4 commits
June 18, 2026 13:49
- Extend Kotlin/Native generator to handle optional parameters (`T?`) and returns, including memory management for optional pointer values. - Add support for mapping optional types in `KotlinType` and logic for parameter and return thunk generation. - Update integration and unit tests for functions with optional parameters, returns, and combinations of both. - Refactor `CDeclLowering` to correctly handle stripped (non-optional) signatures for optional return types.
…lin/Native - Extend generator to handle `String?` parameters and returns, including memory management for nullable C string pointers. - Add thunk generation logic for `UnsafePointer<Int8>?` parameters and `String?` return conversions. - Update integration and unit tests to cover optional `String` interop cases (`String?` params, returns, and combinations). - Refactor existing lowering logic to strip optional `String?` types for correct C declaration processing.
…nal `String?` support - Consolidate handling of optional `String?` parameters and return types. - Introduce utility functions (`isStringType`, `isVoidType`, `isOptionalString`, etc.) for streamlined type checks. - Simplify thunk generation by removing redundant logic and employing new utility functions. - Update tests to reflect changes in import management and `String?` interop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Kotlin natively supports nullable types (
T?), so Swift optionals map cleanly to Kotlin nullabletypes. The challenge is the C ABI layer between Swift and Kotlin/Native:
present.
null if the Swift function returned
nil. The Kotlin wrapper must free the pointer after copying.Stringis not a C-compatible type; it is passedand returned as a null-terminated C string (
char*), soString?needs a dedicated nullablepointer strategy.
The work was delivered in two phases:
Int?,Int32?,Bool?,Double?, unsigned variantsString?as both parameter and returnSupported Types
Int?Long?Int32?Int?Int16?Short?Int8?Byte?UInt?ULong?UInt32?UInt?UInt16?UShort?UInt8?UByte?Bool?Boolean?Float?Float?Double?Double?String?String?Optional<Array>and nested optionals (e.g.,Int??) are not supported and are skipped with acomment.
Phase 1: Optional Parameters
ABI
The shared FFM lowering (
lowerOptionalParameterinFFMSwift2JavaGenerator+FunctionLowering.swift) lowers aT?parameter whereThas a 1:1 Crepresentation to
UnsafePointer<T>?(nullable pointer). The C declaration is therefore:A null pointer means the argument was absent; a valid pointer means the caller stored the value at
that address.
Swift thunk
The shared
cdeclThunkpath handles optional parameters correctly — no custom thunk is needed.The generated thunk:
Kotlin wrapper
The Kotlin call-site must pass a nullable pointer. The strategy differs by signedness:
Signed / float —
cValuesOf(it)creates a single-elementCValues<TVar>on the caller'sstack. The type is inferred from the value; no explicit
Varname is needed:Unsigned and Boolean —
cValuesOfdoes not have overloads for unsigned types orBoolean.Instead, a single-element array is created and its address is taken via
refTo(0):The helper
callArgForOptional(_:value:)inKotlinNativeSwift2KotlinGenerator.swiftencodesthis mapping:
Phase 2: Optional Returns (Numeric / Boolean / Float)
Why CDeclLowering is not used for optional returns
lowerResultinFFMSwift2JavaGenerator+FunctionLowering.swiftexplicitly rejects optionalreturns:
This is a Java/FFM limitation: Java has no nullable scalar types. Kotlin/Native does, so the
Kotlin/Native generator implements its own return strategy.
ABI
The Swift thunk heap-allocates a value of type
Tand returns a pointer to it. A null pointermeans the Swift function returned
nil. The caller (Kotlin) mustfreethe pointer after copyingthe value.
CType(viaCRepresentation.swift:34) unwrapsUnsafeMutablePointer<T>?toT*in C, sothe C header declaration is identical to any other nullable pointer.
Swift thunk
optionalReturningThunkinKotlinNativeSwift2KotlinGenerator+SwiftThunkPrinting.swiftgeneratesthe heap-allocation pattern. The type name is derived directly from the nominal type declaration
(
nom.nominalTypeDecl.name) — no hardcoded mapping table is needed:CDeclLowering signature stripping
Because
lowerFunctionSignaturethrows on optional return types,resolve()andwriteSwiftThunkSourcesboth detect an optional return and lower a stripped signature (with thewrapped, non-optional type as the return) before building the custom
CFunctionmanually:Kotlin wrapper
ptr.pointed.valuedereferences the heap pointer to obtain the scalar value.return nullandreturn resultare non-local returns, valid here because any enclosingusePinnedormemScopedlambdas are
inline.String Optionals
Why String needs separate handling
Stringis not a C-compatible type. The existing non-optionalStringABI passes strings asheap-allocated null-terminated C strings (
char*):UnsafePointer<Int8>in the thunk; Kotlin passes.cstr.UnsafeMutablePointer<Int8>/UnsafeMutablePointer<CChar>in the thunk; thecaller owns the allocation and must
freeit.lowerOptionalParameterinFFMSwift2JavaGenerator+FunctionLowering.swiftexplicitly throws forString?:And the general optional parameter path would produce
UnsafeRawPointer?for non-C types (notUnsafePointer<Int8>?). Both make the standard lowering path unusable forString?.Signature stripping for String? parameters
Like optional returns,
String?parameters are stripped (replaced with non-optionalString)before
CDeclLoweringis called. This makes the lowering succeed and producesUnsafePointer<Int8>(non-optional) in the lowered signature:The C header then shows
const int8_t*— which Kotlin/Native cinterop treats as a nullableCValuesRef<ByteVar>?, allowing null to be passed.String?as a parameter — Kotlin wrapperThe call-site uses Kotlin's safe-call operator on
.cstr. Whensisnull,s?.cstrevaluatesto
null, which the cinterop layer maps to a null pointer:String?as a parameter — Swift thunkstringOptionalAwareThunkgenerates the thunk. The parameter is declared asUnsafePointer<Int8>?(optional pointer) and converted via.map { String(cString: $0) }:String?as a return type — ABIThe ABI mirrors non-optional
String: the thunk returns a heap-allocated null-terminated C string(
char*), withnullmeaning the Swift function returnednil. The C declaration is:For
String?returns, the stripped signature (withStringas the return type) already producesthe right
int8_t*C type via CDeclLowering, so no customCFunctionconstruction is needed —the
lowered.cdeclSignatureis used directly (same as thedefaultbranch).String?as a return type — Swift thunkstringOptionalAwareThunkuses_swiftjava_stringToCString(fromSwiftRuntimeFunctions) forthe heap allocation, avoiding a duplicated allocation loop:
String?as a return type — Kotlin wrapperThis is identical to a non-optional
Stringreturn except?: return nullreplaces?: return "".Combined:
String?param +String?returnstringOptionalAwareThunkhandles this combination in a single pass: