Skip to content

Commit 1f9dd05

Browse files
authored
Support tuples from Swift -> Rust (#211)
1 parent 53b93f4 commit 1f9dd05

File tree

5 files changed

+192
-2
lines changed

5 files changed

+192
-2
lines changed

SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Tuple.swift

+8
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,11 @@ import Foundation
1010
func swift_reflect_tuple_primitives(arg: (Int32, UInt32)) -> (Int32, UInt32) {
1111
arg
1212
}
13+
14+
func swift_reflect_opaque_and_primitive_tuple(arg: (TupleTestOpaqueRustType, Int32)) -> (TupleTestOpaqueRustType, Int32) {
15+
arg
16+
}
17+
18+
func swift_reflect_struct_and_enum_and_string(arg: (TupleTestStruct, TupleTestEnum, RustString)) -> (TupleTestStruct, TupleTestEnum, RustString) {
19+
arg
20+
}

crates/swift-bridge-ir/src/bridged_type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1864,7 +1864,7 @@ impl BridgedType {
18641864
},
18651865
BridgedType::Foreign(ty) => match ty {
18661866
CustomBridgedType::Shared(ty) => match ty {
1867-
SharedType::Struct(_ty) => todo!(),
1867+
SharedType::Struct(ty) => ty.name.to_string(),
18681868
SharedType::Enum(ty) => ty.name.to_string(),
18691869
},
18701870
},

crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,10 @@ impl BridgeableType for OpaqueForeignType {
315315
expression, expression
316316
)
317317
} else {
318-
todo!()
318+
format!(
319+
"{{{}.isOwned = false; return {}.ptr;}}()",
320+
expression, expression
321+
)
319322
}
320323
}
321324
TypePosition::SharedStructField => {

crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs

+148
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,151 @@ func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$I32U8) -> __sw
341341
.test();
342342
}
343343
}
344+
345+
/// Verify that we can use a (opaque type, String) as Swift function arg and return type.
346+
mod extern_swift_tuple_opaque_and_string {
347+
use super::*;
348+
349+
fn bridge_module_tokens() -> TokenStream {
350+
quote! {
351+
#[swift_bridge::bridge]
352+
mod ffi {
353+
extern "Rust" {
354+
type SomeType;
355+
}
356+
extern "Swift" {
357+
fn some_function(arg: (SomeType, String)) -> (SomeType, String);
358+
}
359+
}
360+
}
361+
}
362+
363+
fn expected_rust_tokens() -> ExpectedRustTokens {
364+
ExpectedRustTokens::ContainsMany(vec![
365+
quote! {
366+
pub fn some_function (arg: (super::SomeType, String)) -> (super::SomeType, String) {
367+
{
368+
let val = unsafe { __swift_bridge__some_function ({ let val = arg ; __swift_bridge__tuple_SomeTypeString (Box::into_raw(Box::new({
369+
let val: super::SomeType = val.0;
370+
val
371+
})) as *mut super::SomeType , swift_bridge::string::RustString(val.1).box_into_raw()) }) };
372+
(unsafe { * Box::from_raw(val.0) }, unsafe { Box::from_raw(val.1).0 })
373+
}
374+
}
375+
},
376+
quote! {
377+
#[link_name = "__swift_bridge__$some_function"]
378+
fn __swift_bridge__some_function(arg: __swift_bridge__tuple_SomeTypeString) -> __swift_bridge__tuple_SomeTypeString;
379+
},
380+
quote! {
381+
#[repr(C)]
382+
#[doc(hidden)]
383+
pub struct __swift_bridge__tuple_SomeTypeString(*mut super::SomeType, *mut swift_bridge::string::RustString);
384+
},
385+
])
386+
}
387+
388+
fn expected_swift_code() -> ExpectedSwiftCode {
389+
ExpectedSwiftCode::ContainsManyAfterTrim(vec![
390+
r#"
391+
@_cdecl("__swift_bridge__$some_function")
392+
func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeTypeString) -> __swift_bridge__$tuple$SomeTypeString {
393+
{ let val = some_function(arg: { let val = arg; return (SomeType(ptr: val._0), RustString(ptr: val._1)); }()); return __swift_bridge__$tuple$SomeTypeString(_0: {val.0.isOwned = false; return val.0.ptr;}(), _1: { let rustString = val.1.intoRustString(); rustString.isOwned = false; return rustString.ptr }()); }()
394+
}
395+
"#,
396+
])
397+
}
398+
399+
fn expected_c_header() -> ExpectedCHeader {
400+
ExpectedCHeader::ContainsManyAfterTrim(vec![
401+
r#"typedef struct __swift_bridge__$tuple$SomeTypeString { void* _0; void* _1; } __swift_bridge__$tuple$SomeTypeString;
402+
"#,
403+
])
404+
}
405+
406+
#[test]
407+
fn extern_swift_tuple_opaque_and_string() {
408+
CodegenTest {
409+
bridge_module: bridge_module_tokens().into(),
410+
expected_rust_tokens: expected_rust_tokens(),
411+
expected_swift_code: expected_swift_code(),
412+
expected_c_header: expected_c_header(),
413+
}
414+
.test();
415+
}
416+
}
417+
418+
/// Verify that we can use a (transparent struct type, transparent enum type) as Swift function arg and return type.
419+
mod extern_swift_tuple_transparent_struct_and_transparent_enum {
420+
use super::*;
421+
422+
fn bridge_module_tokens() -> TokenStream {
423+
quote! {
424+
#[swift_bridge::bridge]
425+
mod ffi {
426+
enum SomeEnum {
427+
Variant1,
428+
Variant2,
429+
}
430+
#[swift_bridge(swift_repr = "struct")]
431+
struct SomeStruct {
432+
field: u8
433+
}
434+
extern "Swift" {
435+
fn some_function(arg: (SomeStruct, SomeEnum)) -> (SomeStruct, SomeEnum);
436+
}
437+
}
438+
}
439+
}
440+
441+
fn expected_rust_tokens() -> ExpectedRustTokens {
442+
ExpectedRustTokens::ContainsMany(vec![
443+
quote! {
444+
pub fn some_function (arg: (SomeStruct, SomeEnum)) -> (SomeStruct, SomeEnum) {
445+
{
446+
let val = unsafe { __swift_bridge__some_function ({ let val = arg ; __swift_bridge__tuple_SomeStructSomeEnum (val.0.into_ffi_repr(), val.1.into_ffi_repr()) }) };
447+
(val.0.into_rust_repr(), val.1.into_rust_repr())
448+
}
449+
}
450+
},
451+
quote! {
452+
#[link_name = "__swift_bridge__$some_function"]
453+
fn __swift_bridge__some_function(arg: __swift_bridge__tuple_SomeStructSomeEnum) -> __swift_bridge__tuple_SomeStructSomeEnum;
454+
},
455+
quote! {
456+
#[repr(C)]
457+
#[doc(hidden)]
458+
pub struct __swift_bridge__tuple_SomeStructSomeEnum(__swift_bridge__SomeStruct, __swift_bridge__SomeEnum);
459+
},
460+
])
461+
}
462+
463+
fn expected_swift_code() -> ExpectedSwiftCode {
464+
ExpectedSwiftCode::ContainsManyAfterTrim(vec![
465+
r#"
466+
@_cdecl("__swift_bridge__$some_function")
467+
func __swift_bridge__some_function (_ arg: __swift_bridge__$tuple$SomeStructSomeEnum) -> __swift_bridge__$tuple$SomeStructSomeEnum {
468+
{ let val = some_function(arg: { let val = arg; return (val._0.intoSwiftRepr(), val._1.intoSwiftRepr()); }()); return __swift_bridge__$tuple$SomeStructSomeEnum(_0: val.0.intoFfiRepr(), _1: val.1.intoFfiRepr()); }()
469+
}
470+
"#,
471+
])
472+
}
473+
474+
fn expected_c_header() -> ExpectedCHeader {
475+
ExpectedCHeader::ContainsManyAfterTrim(vec![
476+
r#"typedef struct __swift_bridge__$tuple$SomeStructSomeEnum { struct __swift_bridge__$SomeStruct _0; struct __swift_bridge__$SomeEnum _1; } __swift_bridge__$tuple$SomeStructSomeEnum;
477+
"#,
478+
])
479+
}
480+
481+
#[test]
482+
fn extern_swift_tuple_transparent_struct_and_transparent_enum() {
483+
CodegenTest {
484+
bridge_module: bridge_module_tokens().into(),
485+
expected_rust_tokens: expected_rust_tokens(),
486+
expected_swift_code: expected_swift_code(),
487+
expected_c_header: expected_c_header(),
488+
}
489+
.test();
490+
}
491+
}

crates/swift-integration-tests/src/tuple.rs

+31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
#[swift_bridge::bridge]
22
mod ffi {
3+
enum TupleTestEnum {
4+
NamedField { data: i32 },
5+
UnnamedFields(u8, String),
6+
NoFields,
7+
}
8+
#[swift_bridge(swift_repr = "struct")]
9+
struct TupleTestStruct {
10+
field: u8,
11+
}
312
extern "Rust" {
413
type TupleTestOpaqueRustType;
514
#[swift_bridge(init)]
@@ -15,6 +24,12 @@ mod ffi {
1524
}
1625
extern "Swift" {
1726
fn swift_reflect_tuple_primitives(arg: (i32, u32)) -> (i32, u32);
27+
fn swift_reflect_opaque_and_primitive_tuple(
28+
arg: (TupleTestOpaqueRustType, i32),
29+
) -> (TupleTestOpaqueRustType, i32);
30+
fn swift_reflect_struct_and_enum_and_string(
31+
arg: (TupleTestStruct, TupleTestEnum, String),
32+
) -> (TupleTestStruct, TupleTestEnum, String);
1833
}
1934
extern "Rust" {
2035
fn test_rust_calls_swift_tuples();
@@ -50,4 +65,20 @@ fn test_rust_calls_swift_tuples() {
5065
let val = ffi::swift_reflect_tuple_primitives((-123, 123));
5166
assert_eq!(val.0, -123);
5267
assert_eq!(val.1, 123);
68+
69+
let val = ffi::swift_reflect_opaque_and_primitive_tuple((TupleTestOpaqueRustType(123), -123));
70+
assert_eq!(val.0 .0, 123);
71+
assert_eq!(val.1, -123);
72+
73+
let val = ffi::swift_reflect_struct_and_enum_and_string((
74+
ffi::TupleTestStruct { field: 123 },
75+
ffi::TupleTestEnum::NamedField { data: -123 },
76+
"hello, world".to_string(),
77+
));
78+
assert_eq!(val.0.field, 123);
79+
assert!(matches!(
80+
val.1,
81+
ffi::TupleTestEnum::NamedField { data: -123 }
82+
));
83+
assert_eq!(val.2, "hello, world".to_string());
5384
}

0 commit comments

Comments
 (0)