@@ -177,37 +177,64 @@ public enum WasmCoreType: String, Codable, Sendable {
177177
178178// MARK: - ABI Descriptor
179179
180+ /// How `Optional<T>` is represented at the WASM ABI boundary.
181+ ///
182+ /// The convention is derived from T's descriptor and determines how codegen
183+ /// handles nullable values in both the parameter and return directions.
184+ /// Stack ABI is the general-purpose fallback that works for any T.
185+ public enum OptionalConvention : Sendable , Equatable {
186+ /// Everything goes through the stack (isSome flag + payload pushed/popped).
187+ /// Used for types whose base representation already uses the stack (struct, array, dictionary).
188+ /// This is also the default fallback for unknown types.
189+ case stackABI
190+
191+ /// isSome is passed as an inline WASM parameter alongside T's normal parameters.
192+ /// For returns, T's return type carries the value (no side channel needed).
193+ /// Used for types with compact representations where the value space has no sentinel
194+ /// (bool, jsValue, closure, caseEnum, associatedValueEnum).
195+ case inlineFlag
196+
197+ /// Return value goes through a side-channel storage variable; WASM function returns void.
198+ /// For parameters, behaves like `.inlineFlag` (isSome + T's params as direct WASM params).
199+ /// Used for scalar types where Optional return needs disambiguation (int, string, jsObject, etc.).
200+ case sideChannelReturn
201+ }
202+
180203/// Captures the WASM ABI shape for a ``BridgeType`` so codegen can read descriptor fields
181204/// instead of switching on every concrete type.
182205///
183- /// `wasmReturnType` is for the export direction (Swift→ JS), `importReturnType` for import
184- /// (JS→ Swift). They differ for types like `string` and `associatedValueEnum` where the
206+ /// `wasmReturnType` is for the export direction (Swift-> JS), `importReturnType` for import
207+ /// (JS-> Swift). They differ for types like `string` and `associatedValueEnum` where the
185208/// import side returns a pointer/ID while the export side uses the stack.
186209public struct BridgeTypeDescriptor : Sendable {
187210 public let wasmParams : [ ( name: String , type: WasmCoreType ) ]
188211 public let wasmReturnType : WasmCoreType ?
189212 public let importReturnType : WasmCoreType ?
190- public let optionalUsesStackABI : Bool
191- public let optionalUsesSideChannelReturn : Bool
213+ public let optionalConvention : OptionalConvention
192214 public let usesStackLifting : Bool
193215 public let accessorTransform : AccessorTransform
194216 public let lowerMethod : LowerMethod
195217
218+ /// Creates a descriptor with an explicitly specified optional convention.
219+ ///
220+ /// When `optionalConvention` is nil, it is derived from `wasmParams`:
221+ /// - Empty `wasmParams` (stack-based types) default to `.stackABI`
222+ /// - Non-empty `wasmParams` (scalar types) default to `.inlineFlag`
223+ ///
224+ /// Only `.sideChannelReturn` needs to be explicitly specified.
196225 public init (
197226 wasmParams: [ ( name: String , type: WasmCoreType ) ] ,
198227 wasmReturnType: WasmCoreType ? ,
199228 importReturnType: WasmCoreType ? ? = nil ,
200- optionalUsesStackABI: Bool ,
201- optionalUsesSideChannelReturn: Bool = false ,
229+ optionalConvention: OptionalConvention ? = nil ,
202230 usesStackLifting: Bool = false ,
203231 accessorTransform: AccessorTransform ,
204232 lowerMethod: LowerMethod
205233 ) {
206234 self . wasmParams = wasmParams
207235 self . wasmReturnType = wasmReturnType
208236 self . importReturnType = importReturnType ?? wasmReturnType
209- self . optionalUsesStackABI = optionalUsesStackABI
210- self . optionalUsesSideChannelReturn = optionalUsesSideChannelReturn
237+ self . optionalConvention = optionalConvention ?? ( wasmParams. isEmpty ? . stackABI : . inlineFlag)
211238 self . usesStackLifting = usesStackLifting
212239 self . accessorTransform = accessorTransform
213240 self . lowerMethod = lowerMethod
@@ -291,43 +318,38 @@ extension BridgeType {
291318 return BridgeTypeDescriptor (
292319 wasmParams: [ ( " value " , . i32) ] ,
293320 wasmReturnType: . i32,
294- optionalUsesStackABI: false ,
295321 accessorTransform: . identity,
296322 lowerMethod: . stackReturn
297323 )
298324 case . int:
299325 return BridgeTypeDescriptor (
300326 wasmParams: [ ( " value " , . i32) ] ,
301327 wasmReturnType: . i32,
302- optionalUsesStackABI: false ,
303- optionalUsesSideChannelReturn: true ,
328+ optionalConvention: . sideChannelReturn,
304329 accessorTransform: . identity,
305330 lowerMethod: . stackReturn
306331 )
307332 case . uint:
308333 return BridgeTypeDescriptor (
309334 wasmParams: [ ( " value " , . i32) ] ,
310335 wasmReturnType: . i32,
311- optionalUsesStackABI: false ,
312- optionalUsesSideChannelReturn: true ,
336+ optionalConvention: . sideChannelReturn,
313337 accessorTransform: . identity,
314338 lowerMethod: . stackReturn
315339 )
316340 case . float:
317341 return BridgeTypeDescriptor (
318342 wasmParams: [ ( " value " , . f32) ] ,
319343 wasmReturnType: . f32,
320- optionalUsesStackABI: false ,
321- optionalUsesSideChannelReturn: true ,
344+ optionalConvention: . sideChannelReturn,
322345 accessorTransform: . identity,
323346 lowerMethod: . stackReturn
324347 )
325348 case . double:
326349 return BridgeTypeDescriptor (
327350 wasmParams: [ ( " value " , . f64) ] ,
328351 wasmReturnType: . f64,
329- optionalUsesStackABI: false ,
330- optionalUsesSideChannelReturn: true ,
352+ optionalConvention: . sideChannelReturn,
331353 accessorTransform: . identity,
332354 lowerMethod: . stackReturn
333355 )
@@ -336,8 +358,7 @@ extension BridgeType {
336358 wasmParams: [ ( " bytes " , . i32) , ( " length " , . i32) ] ,
337359 wasmReturnType: nil ,
338360 importReturnType: . i32,
339- optionalUsesStackABI: false ,
340- optionalUsesSideChannelReturn: true ,
361+ optionalConvention: . sideChannelReturn,
341362 accessorTransform: . identity,
342363 lowerMethod: . stackReturn
343364 )
@@ -347,49 +368,43 @@ extension BridgeType {
347368 return BridgeTypeDescriptor (
348369 wasmParams: [ ( " value " , . i32) ] ,
349370 wasmReturnType: . i32,
350- optionalUsesStackABI: false ,
351- optionalUsesSideChannelReturn: true ,
371+ optionalConvention: . sideChannelReturn,
352372 accessorTransform: transform,
353373 lowerMethod: . stackReturn
354374 )
355375 case . jsValue:
356376 return BridgeTypeDescriptor (
357377 wasmParams: [ ( " kind " , . i32) , ( " payload1 " , . i32) , ( " payload2 " , . f64) ] ,
358378 wasmReturnType: nil ,
359- optionalUsesStackABI: false ,
360379 accessorTransform: . identity,
361380 lowerMethod: . stackReturn
362381 )
363382 case . swiftHeapObject:
364383 return BridgeTypeDescriptor (
365384 wasmParams: [ ( " pointer " , . pointer) ] ,
366385 wasmReturnType: . pointer,
367- optionalUsesStackABI: false ,
368386 accessorTransform: . identity,
369387 lowerMethod: . stackReturn
370388 )
371389 case . unsafePointer:
372390 return BridgeTypeDescriptor (
373391 wasmParams: [ ( " pointer " , . pointer) ] ,
374392 wasmReturnType: . pointer,
375- optionalUsesStackABI: false ,
376393 accessorTransform: . identity,
377394 lowerMethod: . stackReturn
378395 )
379396 case . swiftProtocol( let protocolName) :
380397 return BridgeTypeDescriptor (
381398 wasmParams: [ ( " value " , . i32) ] ,
382399 wasmReturnType: . i32,
383- optionalUsesStackABI: false ,
384- optionalUsesSideChannelReturn: true ,
400+ optionalConvention: . sideChannelReturn,
385401 accessorTransform: . cast( " Any \( protocolName) " ) ,
386402 lowerMethod: . stackReturn
387403 )
388404 case . caseEnum:
389405 return BridgeTypeDescriptor (
390406 wasmParams: [ ( " value " , . i32) ] ,
391407 wasmReturnType: . i32,
392- optionalUsesStackABI: false ,
393408 accessorTransform: . identity,
394409 lowerMethod: . stackReturn
395410 )
@@ -400,43 +415,38 @@ extension BridgeType {
400415 wasmParams: [ ( " bytes " , . i32) , ( " length " , . i32) ] ,
401416 wasmReturnType: nil ,
402417 importReturnType: . some( . i32) ,
403- optionalUsesStackABI: false ,
404- optionalUsesSideChannelReturn: true ,
418+ optionalConvention: . sideChannelReturn,
405419 accessorTransform: . identity,
406420 lowerMethod: . stackReturn
407421 )
408422 case . float:
409423 return BridgeTypeDescriptor (
410424 wasmParams: [ ( " value " , . f32) ] ,
411425 wasmReturnType: . f32,
412- optionalUsesStackABI: false ,
413- optionalUsesSideChannelReturn: true ,
426+ optionalConvention: . sideChannelReturn,
414427 accessorTransform: . identity,
415428 lowerMethod: . stackReturn
416429 )
417430 case . double:
418431 return BridgeTypeDescriptor (
419432 wasmParams: [ ( " value " , . f64) ] ,
420433 wasmReturnType: . f64,
421- optionalUsesStackABI: false ,
422- optionalUsesSideChannelReturn: true ,
434+ optionalConvention: . sideChannelReturn,
423435 accessorTransform: . identity,
424436 lowerMethod: . stackReturn
425437 )
426438 case . bool:
427439 return BridgeTypeDescriptor (
428440 wasmParams: [ ( " value " , . i32) ] ,
429441 wasmReturnType: . i32,
430- optionalUsesStackABI: false ,
431442 accessorTransform: . identity,
432443 lowerMethod: . stackReturn
433444 )
434445 case . int, . int32, . int64, . uint, . uint32, . uint64:
435446 return BridgeTypeDescriptor (
436447 wasmParams: [ ( " value " , . i32) ] ,
437448 wasmReturnType: . i32,
438- optionalUsesStackABI: false ,
439- optionalUsesSideChannelReturn: true ,
449+ optionalConvention: . sideChannelReturn,
440450 accessorTransform: . identity,
441451 lowerMethod: . stackReturn
442452 )
@@ -446,7 +456,6 @@ extension BridgeType {
446456 wasmParams: [ ( " caseId " , . i32) ] ,
447457 wasmReturnType: nil ,
448458 importReturnType: . i32,
449- optionalUsesStackABI: false ,
450459 usesStackLifting: true ,
451460 accessorTransform: . identity,
452461 lowerMethod: . pushParameter
@@ -455,15 +464,13 @@ extension BridgeType {
455464 return BridgeTypeDescriptor (
456465 wasmParams: [ ( " funcRef " , . i32) ] ,
457466 wasmReturnType: . i32,
458- optionalUsesStackABI: false ,
459467 accessorTransform: . identity,
460468 lowerMethod: . stackReturn
461469 )
462470 case . swiftStruct:
463471 return BridgeTypeDescriptor (
464472 wasmParams: [ ] ,
465473 wasmReturnType: nil ,
466- optionalUsesStackABI: true ,
467474 usesStackLifting: true ,
468475 accessorTransform: . identity,
469476 lowerMethod: . fullReturn
@@ -472,7 +479,6 @@ extension BridgeType {
472479 return BridgeTypeDescriptor (
473480 wasmParams: [ ] ,
474481 wasmReturnType: nil ,
475- optionalUsesStackABI: true ,
476482 usesStackLifting: true ,
477483 accessorTransform: . identity,
478484 lowerMethod: . fullReturn
@@ -481,15 +487,13 @@ extension BridgeType {
481487 return BridgeTypeDescriptor (
482488 wasmParams: [ ] ,
483489 wasmReturnType: nil ,
484- optionalUsesStackABI: true ,
485490 accessorTransform: . identity,
486491 lowerMethod: . fullReturn
487492 )
488493 case . void, . namespaceEnum:
489494 return BridgeTypeDescriptor (
490495 wasmParams: [ ] ,
491496 wasmReturnType: nil ,
492- optionalUsesStackABI: false ,
493497 accessorTransform: . identity,
494498 lowerMethod: . none
495499 )
@@ -1473,7 +1477,7 @@ extension BridgeType {
14731477 guard case . nullable = self else {
14741478 return false
14751479 }
1476- return descriptor. optionalUsesSideChannelReturn
1480+ return descriptor. optionalConvention == . sideChannelReturn
14771481 }
14781482}
14791483
0 commit comments