diff --git a/Cargo.toml b/Cargo.toml index 563a7016..4d07ea75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ default = [] # Enables bridging of async functions. async = ["tokio", "once_cell"] +# Prioritises compatibility over performance +compatibility = ["swift-bridge-macro/compatibility"] + [build-dependencies] swift-bridge-build = {version = "0.1.51", path = "crates/swift-bridge-build"} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift index 2f0e10ba..2550f455 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift @@ -92,6 +92,34 @@ class VecTests: XCTestCase { XCTAssertEqual(reflected.get(index: 0)!, TransparentEnumInsideVecT.VariantB) XCTAssertEqual(reflected.pop()!, TransparentEnumInsideVecT.VariantB) } + + /// Verify that a Vec of transparent struct with derive(Copy, Clone) can be used + /// as an argument and return type for extern "Rust" functions. + func testReflectVecOfTransparentStructFromCopy() throws { + let vec: RustVec = RustVec() + vec.push(value: TransparentStructInsideVecTWithCopy(integer: 10)) + + let reflected = rust_reflect_vec_transparent_struct_with_copy(vec) + XCTAssertEqual(reflected.len(), 1) + XCTAssertEqual(reflected.get(index: 0)!.integer, 10) + let popped = try XCTUnwrap(reflected.pop()) + XCTAssertEqual(popped.integer, 10) + } + + /// Verify that a Vec of transparent struct with derive(Clone) can be used as an + /// argument and return type for extern "Rust" functions. + func testReflectVecOfTransparentStructFromClone() throws { + let vec: RustVec = RustVec() + vec.push(value: TransparentStructInsideVecT(string: "string".intoRustString(), integer: 10)) + + let reflected = rust_reflect_vec_transparent_struct(vec) + XCTAssertEqual(reflected.len(), 1) + XCTAssertEqual(reflected.get(index: 0)!.string.toString(), "string") + XCTAssertEqual(reflected.get(index: 0)!.integer, 10) + let popped = try XCTUnwrap(reflected.pop()) + XCTAssertEqual(popped.string.toString(), "string") + XCTAssertEqual(popped.integer, 10) + } /// Verify that we can construct a RustVec of every primitive type. /// We tested all of the methods on two different primitives above to be sure that our diff --git a/crates/swift-bridge-ir/Cargo.toml b/crates/swift-bridge-ir/Cargo.toml index e99f4a32..ec3c16ca 100644 --- a/crates/swift-bridge-ir/Cargo.toml +++ b/crates/swift-bridge-ir/Cargo.toml @@ -7,6 +7,11 @@ description = "Holds the data structures and logic for bridge module parsing and repository = "https://github.com/chinedufn/swift-bridge" license = "Apache-2.0/MIT" +[features] + +# Prioritises compatibility over performance +compatibility = [] + [dependencies] proc-macro2 = "1" quote = "1" diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs index d34dbc87..81f12143 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_struct_codegen_tests.rs @@ -73,7 +73,7 @@ extension __swift_bridge__$SomeStruct { } fn expected_c_header() -> ExpectedCHeader { - ExpectedCHeader::ExactAfterTrim( + ExpectedCHeader::ContainsAfterTrim( r#" #include #include @@ -680,15 +680,15 @@ func some_function(_ arg: Optional) -> Optional { } fn expected_c_header() -> ExpectedCHeader { - ExpectedCHeader::ExactAfterTrim( + ExpectedCHeader::ContainsManyAfterTrim(vec![ r#" #include #include typedef struct __swift_bridge__$SomeStruct { uint8_t field; } __swift_bridge__$SomeStruct; typedef struct __swift_bridge__$Option$SomeStruct { bool is_some; __swift_bridge__$SomeStruct val; } __swift_bridge__$Option$SomeStruct; -struct __swift_bridge__$Option$SomeStruct __swift_bridge__$some_function(struct __swift_bridge__$Option$SomeStruct arg); - "#, - ) +"#, + "struct __swift_bridge__$Option$SomeStruct __swift_bridge__$some_function(struct __swift_bridge__$Option$SomeStruct arg);", + ]) } #[test] diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs index 9725d376..15c4bc16 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs @@ -536,3 +536,622 @@ void __swift_bridge__$some_function(void* arg); .test(); } } + +/// Verify that we emit Rust, Swift and C header code that allows +/// a transparent struct with derive(Clone) to be used within a Vec. +#[cfg(feature = "compatibility")] +mod transparent_struct_vec_support { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + #[swift_bridge(swift_repr = "struct")] + #[derive(Clone)] + struct SomeStruct { + string: String, + integer: i64, + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + }; + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +extension SomeStruct: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_SomeStruct$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_SomeStruct$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_SomeStruct$push(vecPtr, value.intoFfiRepr()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$pop(vecPtr) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get_mut(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_SomeStruct$len(vecPtr) + } +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void* __swift_bridge__$Vec_SomeStruct$new(void); +void __swift_bridge__$Vec_SomeStruct$drop(void* vec_ptr); +void __swift_bridge__$Vec_SomeStruct$push(void* vec_ptr, __swift_bridge__$SomeStruct item); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$pop(void* vec_ptr); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get(void* vec_ptr, uintptr_t index); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_SomeStruct$len(void* vec_ptr); +void* __swift_bridge__$Vec_SomeStruct$as_ptr(void* vec_ptr); +"#, + ) + } + + #[test] + fn transparent_struct_vec_support() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} + +/// Verify that we emit Rust, Swift and C header code that allows +/// a transparent struct with derive(Copy, Clone) to be used within a Vec. +mod transparent_struct_vec_support_with_copy { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + #[swift_bridge(swift_repr = "struct")] + #[derive(Copy, Clone)] + struct SomeStruct { + integer: i64, + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + }; + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +extension SomeStruct: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_SomeStruct$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_SomeStruct$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_SomeStruct$push(vecPtr, value.intoFfiRepr()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$pop(vecPtr) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get_mut(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_SomeStruct$len(vecPtr) + } +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void* __swift_bridge__$Vec_SomeStruct$new(void); +void __swift_bridge__$Vec_SomeStruct$drop(void* vec_ptr); +void __swift_bridge__$Vec_SomeStruct$push(void* vec_ptr, __swift_bridge__$SomeStruct item); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$pop(void* vec_ptr); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get(void* vec_ptr, uintptr_t index); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_SomeStruct$len(void* vec_ptr); +void* __swift_bridge__$Vec_SomeStruct$as_ptr(void* vec_ptr); +"#, + ) + } + + #[test] + fn transparent_struct_vec_support_with_copy() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} + +/// Verify that we DON'T emit Rust, Swift and C header code for a transparent +/// struct with no derived traits. +mod transparent_struct_vec_support_without_derives { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + #[swift_bridge(swift_repr = "struct")] + struct SomeStruct { + integer: i64, + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsManyAndDoesNotContainMany { + contains: vec![], + does_not_contain: vec![ + quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + } + }, + quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + } + }, + ], + } + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::DoesNotContainAfterTrim( + r#" +extension SomeStruct: Vectorizable { + public static func vecOfSelfNew() -> UnsafeMutableRawPointer { + __swift_bridge__$Vec_SomeStruct$new() + } + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) { + __swift_bridge__$Vec_SomeStruct$drop(vecPtr) + } + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) { + __swift_bridge__$Vec_SomeStruct$push(vecPtr, value.intoFfiRepr()) + } + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$pop(vecPtr) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional { + let maybeStruct = __swift_bridge__$Vec_SomeStruct$get_mut(vecPtr, index) + return maybeStruct.intoSwiftRepr() + } + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt { + __swift_bridge__$Vec_SomeStruct$len(vecPtr) + } +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::DoesNotContainAfterTrim( + r#" +void* __swift_bridge__$Vec_SomeStruct$new(void); +void __swift_bridge__$Vec_SomeStruct$drop(void* vec_ptr); +void __swift_bridge__$Vec_SomeStruct$push(void* vec_ptr, __swift_bridge__$SomeStruct item); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$pop(void* vec_ptr); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get(void* vec_ptr, uintptr_t index); +__swift_bridge__$Option$SomeStruct __swift_bridge__$Vec_SomeStruct$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_SomeStruct$len(void* vec_ptr); +void* __swift_bridge__$Vec_SomeStruct$as_ptr(void* vec_ptr); +"#, + ) + } + + #[test] + fn transparent_struct_vec_support_without_derives() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} + +/// Test code generation for Rust function that returns a Vec where T is a transparent struct. +#[cfg(feature = "compatibility")] +mod extern_rust_fn_return_vec_of_transparent_struct { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + #[swift_bridge(swift_repr = "struct")] + #[derive(Clone)] + struct SomeStruct { + string: String, + integer: i64, + } + + extern "Rust" { + fn some_function() -> Vec; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + pub extern "C" fn __swift_bridge__some_function() -> *mut Vec { + Box::into_raw(Box::new(super::some_function())) + } + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +func some_function() -> RustVec { + RustVec(ptr: __swift_bridge__$some_function()) +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void* __swift_bridge__$some_function(void); +"#, + ) + } + + #[test] + fn extern_rust_fn_return_vec_of_transparent_struct() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} + +/// Test code generation for Rust function that has an argument +/// Vec where T is a transparent struct. +mod extern_rust_fn_arg_vec_of_transparent_struct { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + #[swift_bridge(swift_repr = "struct")] + #[derive(Clone)] + struct SomeStruct { + string: String, + integer: i64, + } + + extern "Rust" { + type MyRustType; + fn some_function(arg: Vec); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + pub extern "C" fn __swift_bridge__some_function( + arg: *mut Vec + ) { + super::some_function(unsafe { * Box::from_raw(arg) }) + } + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +func some_function(_ arg: RustVec) { + __swift_bridge__$some_function({ let val = arg; val.isOwned = false; return val.ptr }()) +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void __swift_bridge__$some_function(void* arg); +"#, + ) + } + + #[test] + fn extern_rust_fn_arg_vec_of_transparent_struct() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: expected_c_header(), + } + .test(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs index 05871eec..9254fe42 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs @@ -2,6 +2,7 @@ use crate::bridged_type::shared_struct::StructField; use crate::bridged_type::{BridgeableType, BridgedType, StdLibType, StructFields}; +use crate::codegen::generate_rust_tokens::can_generate_vec_of_transparent_struct_functions; use crate::codegen::CodegenConfig; use crate::parse::{SharedTypeDeclaration, TypeDeclaration, TypeDeclarations}; use crate::parsed_extern_fn::ParsedExternFn; @@ -116,14 +117,22 @@ impl SwiftBridgeModule { "".to_string() }; + let vec_support = + if can_generate_vec_of_transparent_struct_functions(&ty_struct) { + vec_transparent_struct_c_support(&name) + } else { + format!("") + }; + let ty_decl = format!( r#"typedef struct {prefix}${name} {{{maybe_fields}}} {prefix}${name}; -typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi_name};"#, +typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi_name};{vec_support}"#, prefix = SWIFT_BRIDGE_PREFIX, ffi_name = ffi_name, option_ffi_name = option_ffi_name, name = name, - maybe_fields = maybe_fields + maybe_fields = maybe_fields, + vec_support = vec_support ); header += &ty_decl; @@ -428,6 +437,22 @@ fn declare_custom_c_ffi_types( } } +fn vec_transparent_struct_c_support(struct_name: &str) -> String { + format!( + r#" +void* __swift_bridge__$Vec_{struct_name}$new(void); +void __swift_bridge__$Vec_{struct_name}$drop(void* vec_ptr); +void __swift_bridge__$Vec_{struct_name}$push(void* vec_ptr, __swift_bridge__${struct_name} item); +__swift_bridge__$Option${struct_name} __swift_bridge__$Vec_{struct_name}$pop(void* vec_ptr); +__swift_bridge__$Option${struct_name} __swift_bridge__$Vec_{struct_name}$get(void* vec_ptr, uintptr_t index); +__swift_bridge__$Option${struct_name} __swift_bridge__$Vec_{struct_name}$get_mut(void* vec_ptr, uintptr_t index); +uintptr_t __swift_bridge__$Vec_{struct_name}$len(void* vec_ptr); +void* __swift_bridge__$Vec_{struct_name}$as_ptr(void* vec_ptr); +"#, + struct_name = struct_name + ) +} + fn declare_func( func: &ParsedExternFn, bookkeeping: &mut Bookkeeping, diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs index 8e710798..aac12078 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs @@ -11,6 +11,8 @@ use crate::bridge_module_attributes::CfgAttr; use crate::parse::{HostLang, SharedTypeDeclaration, TypeDeclaration}; use crate::SwiftBridgeModule; +pub(crate) use self::vec::vec_of_transparent_struct::can_generate_vec_of_transparent_struct_functions; + mod shared_enum; mod shared_struct; mod vec; diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_struct.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_struct.rs index b2a88ef2..e53251de 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_struct.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_struct.rs @@ -2,6 +2,9 @@ //! crates/swift-bridge-ir/src/codegen/codegen_tests/shared_struct_codegen_tests.rs use crate::bridged_type::{BridgedType, SharedStruct}; +use crate::codegen::generate_rust_tokens::vec::vec_of_transparent_struct::{ + can_generate_vec_of_transparent_struct_functions, generate_vec_of_transparent_struct_functions, +}; use crate::{SwiftBridgeModule, SWIFT_BRIDGE_PREFIX}; use proc_macro2::{Span, TokenStream}; use quote::quote; @@ -99,6 +102,12 @@ impl SwiftBridgeModule { derives.push(quote! {Clone}); } + let vec_support = if can_generate_vec_of_transparent_struct_functions(&shared_struct) { + generate_vec_of_transparent_struct_functions(&shared_struct) + } else { + quote! {} + }; + let definition = quote! { #[derive(#(#derives),*)] pub struct #struct_name #struct_fields @@ -153,6 +162,8 @@ impl SwiftBridgeModule { } } } + + #vec_support }; Some(definition) diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec.rs index 1135e38e..4ae7b039 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec.rs @@ -1,2 +1,3 @@ pub(super) mod vec_of_opaque_rust_type; pub(super) mod vec_of_transparent_enum; +pub(super) mod vec_of_transparent_struct; diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec/vec_of_transparent_struct.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec/vec_of_transparent_struct.rs new file mode 100644 index 00000000..5afe3396 --- /dev/null +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/vec/vec_of_transparent_struct.rs @@ -0,0 +1,288 @@ +use crate::bridged_type::{SharedStruct, StructSwiftRepr}; +use proc_macro2::TokenStream; +use quote::quote; + +/// Generate the functions that Swift calls uses inside of the corresponding class for a +/// transparent struct's Vectorizable implementation. +/// +/// So inside of `extension SomeTransparentStruct: Vectorizable {}` on the Swift side. +pub(in super::super) fn generate_vec_of_transparent_struct_functions( + shared_struct: &SharedStruct, +) -> TokenStream { + if can_generate_vec_of_transparent_struct_functions(&shared_struct) { + let struct_name = &shared_struct.name; + + // examples: + // "__swift_bridge__$Vec_SomeTransparentStruct$new" + // "__swift_bridge__$Vec_SomeTransparentStruct$drop" + let make_export_name = |fn_name| { + format!( + "__swift_bridge__$Vec_{}${}", + shared_struct.swift_name_string(), + fn_name + ) + }; + let export_name_new = make_export_name("new"); + let export_name_drop = make_export_name("drop"); + let export_name_len = make_export_name("len"); + let export_name_get = make_export_name("get"); + let export_name_get_mut = make_export_name("get_mut"); + let export_name_push = make_export_name("push"); + let export_name_pop = make_export_name("pop"); + let export_name_as_ptr = make_export_name("as_ptr"); + + let ffi_struct_repr = &shared_struct.ffi_name_tokens(); + let ffi_option_struct_repr = shared_struct.ffi_option_name_tokens(); + // TODO: Check for trait implementation as well + let vec_map = if shared_struct.derives.copy { + quote! { *v } + } else { + quote! { v.clone() } + }; + + quote! { + const _: () = { + #[doc(hidden)] + #[export_name = #export_name_new] + pub extern "C" fn _new() -> *mut Vec<#struct_name> { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = #export_name_drop] + pub extern "C" fn _drop(vec: *mut Vec<#struct_name>) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = #export_name_len] + pub extern "C" fn _len(vec: *const Vec<#struct_name>) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = #export_name_get] + pub extern "C" fn _get(vec: *const Vec<#struct_name>, index: usize) -> #ffi_option_struct_repr { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v|#vec_map); + #ffi_option_struct_repr::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = #export_name_get_mut] + pub extern "C" fn _get_mut(vec: *mut Vec<#struct_name>, index: usize) -> #ffi_option_struct_repr { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v|#vec_map); + #ffi_option_struct_repr::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = #export_name_push] + pub extern "C" fn _push(vec: *mut Vec<#struct_name>, val: #ffi_struct_repr) { + unsafe { &mut *vec }.push( val.into_rust_repr() ) + } + + #[doc(hidden)] + #[export_name = #export_name_pop] + pub extern "C" fn _pop(vec: *mut Vec<#struct_name>) -> #ffi_option_struct_repr { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + #ffi_option_struct_repr::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = #export_name_as_ptr] + pub extern "C" fn _as_ptr(vec: *const Vec<#struct_name>) -> *const #struct_name { + unsafe { & *vec }.as_ptr() + } + }; + } + } else { + quote! {} + } +} + +pub(crate) fn can_generate_vec_of_transparent_struct_functions( + shared_struct: &SharedStruct, +) -> bool { + match shared_struct.swift_repr { + StructSwiftRepr::Class => false, + // TODO: Check for trait implementation as well + #[cfg(feature = "compatibility")] + StructSwiftRepr::Structure => shared_struct.derives.copy || shared_struct.derives.clone, + #[cfg(not(feature = "compatibility"))] + StructSwiftRepr::Structure => shared_struct.derives.copy, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bridged_type::{shared_struct::StructDerives, StructFields, StructSwiftRepr}; + use crate::test_utils::assert_tokens_eq; + use proc_macro2::{Ident, Span}; + + /// Verify that we can generate the functions for shared struct with derive(Copy, Clone) that + /// gets exposed to Swift in order to power the `extension MyRustType: Vectorizable { }` + /// implementation on the Swift side. + #[test] + fn generates_vectorizable_impl_for_shared_struct_with_copy() { + let expected = quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| *v ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + }; + }; + + let shared_struct = SharedStruct { + name: Ident::new("SomeStruct", Span::call_site()), + swift_repr: StructSwiftRepr::Structure, + fields: StructFields::Named(vec![]), + swift_name: None, + already_declared: false, + derives: StructDerives { + copy: true, + clone: true, + }, + }; + assert_tokens_eq( + &generate_vec_of_transparent_struct_functions(&shared_struct), + &expected, + ); + } + + /// Verify that we can generate the functions for shared struct with derive(Clone) that + /// gets exposed to Swift in order to power the `extension MyRustType: Vectorizable { }` + /// implementation on the Swift side. + #[test] + #[cfg(feature = "compatibility")] + fn generates_vectorizable_impl_for_shared_struct_with_clone() { + let expected = quote! { + const _: () = { + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$new"] + pub extern "C" fn _new() -> *mut Vec { + Box::into_raw(Box::new(Vec::new())) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$drop"] + pub extern "C" fn _drop(vec: *mut Vec) { + let vec = unsafe { Box::from_raw(vec) }; + drop(vec) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$len"] + pub extern "C" fn _len(vec: *const Vec) -> usize { + unsafe { &*vec }.len() + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get"] + pub extern "C" fn _get(vec: *const Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &*vec }; + let val = vec.get(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$get_mut"] + pub extern "C" fn _get_mut(vec: *mut Vec, index: usize) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.get_mut(index).map(|v| v.clone() ); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$push"] + pub extern "C" fn _push(vec: *mut Vec, val: __swift_bridge__SomeStruct) { + unsafe { &mut *vec }.push(val.into_rust_repr()) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$pop"] + pub extern "C" fn _pop(vec: *mut Vec) -> __swift_bridge__Option_SomeStruct { + let vec = unsafe { &mut *vec }; + let val = vec.pop(); + __swift_bridge__Option_SomeStruct::from_rust_repr(val) + } + + #[doc(hidden)] + #[export_name = "__swift_bridge__$Vec_SomeStruct$as_ptr"] + pub extern "C" fn _as_ptr(vec: *const Vec) -> *const SomeStruct { + unsafe { & *vec }.as_ptr() + } + }; + }; + + let shared_struct = SharedStruct { + name: Ident::new("SomeStruct", Span::call_site()), + swift_repr: StructSwiftRepr::Structure, + fields: StructFields::Named(vec![]), + swift_name: None, + already_declared: false, + derives: StructDerives { + copy: false, + clone: true, + }, + }; + assert_tokens_eq( + &generate_vec_of_transparent_struct_functions(&shared_struct), + &expected, + ); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs index 64864f0e..f6521ccd 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs @@ -1,5 +1,6 @@ use crate::bridged_type::shared_struct::StructField; use crate::bridged_type::{BridgedType, SharedStruct, StructFields, StructSwiftRepr, TypePosition}; +use crate::codegen::generate_rust_tokens::can_generate_vec_of_transparent_struct_functions; use crate::SwiftBridgeModule; impl SwiftBridgeModule { @@ -47,6 +48,49 @@ impl SwiftBridgeModule { let convert_ffi_repr_to_swift = shared_struct.convert_ffi_expression_to_swift("self", &self.types); + let vectorizable_impl = if can_generate_vec_of_transparent_struct_functions( + &shared_struct, + ) { + format!( + r#" +extension {struct_name}: Vectorizable {{ + public static func vecOfSelfNew() -> UnsafeMutableRawPointer {{ + __swift_bridge__$Vec_{struct_name}$new() + }} + + public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) {{ + __swift_bridge__$Vec_{struct_name}$drop(vecPtr) + }} + + public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) {{ + __swift_bridge__$Vec_{struct_name}$push(vecPtr, value.intoFfiRepr()) + }} + + public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional {{ + let maybeStruct = __swift_bridge__$Vec_{struct_name}$pop(vecPtr) + return maybeStruct.intoSwiftRepr() + }} + + public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional {{ + let maybeStruct = __swift_bridge__$Vec_{struct_name}$get(vecPtr, index) + return maybeStruct.intoSwiftRepr() + }} + + public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional {{ + let maybeStruct = __swift_bridge__$Vec_{struct_name}$get_mut(vecPtr, index) + return maybeStruct.intoSwiftRepr() + }} + + public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt {{ + __swift_bridge__$Vec_{struct_name}$len(vecPtr) + }} +}}"#, + struct_name = struct_name + ) + } else { + format!("") + }; + // No need to generate any code. Swift will automatically generate a // struct from our C header typedef that we generate for this struct. let swift_struct = format!( @@ -82,7 +126,7 @@ extension {option_ffi_name} {{ return {option_ffi_name}(is_some: false, val: {ffi_repr_name}()) }} }} -}}"#, +}}{vectorizable_impl}"#, struct_name = struct_name, initializer_params = initializer_params, initializer_body = initializer_body, @@ -90,7 +134,8 @@ extension {option_ffi_name} {{ ffi_repr_name = shared_struct.ffi_name_string(), option_ffi_name = option_ffi_name, convert_swift_to_ffi_repr = convert_swift_to_ffi_repr, - convert_ffi_repr_to_swift = convert_ffi_repr_to_swift + convert_ffi_repr_to_swift = convert_ffi_repr_to_swift, + vectorizable_impl = vectorizable_impl ); Some(swift_struct) diff --git a/crates/swift-bridge-macro/Cargo.toml b/crates/swift-bridge-macro/Cargo.toml index 291d3839..32c61569 100644 --- a/crates/swift-bridge-macro/Cargo.toml +++ b/crates/swift-bridge-macro/Cargo.toml @@ -10,6 +10,11 @@ license = "Apache-2.0/MIT" [lib] proc-macro = true +[features] + +# Prioritises compatibility over performance +compatibility = ["swift-bridge-ir/compatibility"] + [dependencies] proc-macro2 = "1" quote = "1" diff --git a/crates/swift-integration-tests/Cargo.toml b/crates/swift-integration-tests/Cargo.toml index 23aa0323..fe9a9929 100644 --- a/crates/swift-integration-tests/Cargo.toml +++ b/crates/swift-integration-tests/Cargo.toml @@ -19,4 +19,4 @@ crate-type = ["staticlib"] swift-bridge-build = {path = "../swift-bridge-build"} [dependencies] -swift-bridge = {path = "../../", features = ["async"]} +swift-bridge = {path = "../../", features = ["async", "compatibility"]} diff --git a/crates/swift-integration-tests/src/vec.rs b/crates/swift-integration-tests/src/vec.rs index 45b8c04c..374bd8e6 100644 --- a/crates/swift-integration-tests/src/vec.rs +++ b/crates/swift-integration-tests/src/vec.rs @@ -5,6 +5,19 @@ mod ffi { VariantB, } + #[swift_bridge(swift_repr = "struct")] + #[derive(Clone)] + struct TransparentStructInsideVecT { + string: String, + integer: i64, + } + + #[swift_bridge(swift_repr = "struct")] + #[derive(Copy, Clone)] + struct TransparentStructInsideVecTWithCopy { + integer: i64, + } + extern "Rust" { type ARustTypeInsideVecT; @@ -25,6 +38,18 @@ mod ffi { arg: Vec, ) -> Vec; } + + extern "Rust" { + fn rust_reflect_vec_transparent_struct( + arg: Vec, + ) -> Vec; + } + + extern "Rust" { + fn rust_reflect_vec_transparent_struct_with_copy( + arg: Vec, + ) -> Vec; + } } pub struct ARustTypeInsideVecT { @@ -52,3 +77,15 @@ fn rust_reflect_vec_transparent_enum( ) -> Vec { arg } + +fn rust_reflect_vec_transparent_struct( + arg: Vec, +) -> Vec { + arg +} + +fn rust_reflect_vec_transparent_struct_with_copy( + arg: Vec, +) -> Vec { + arg +}