From 235c46054ae7cd65a6d853042237110b6968d7b5 Mon Sep 17 00:00:00 2001 From: BrightShard Date: Sat, 27 Apr 2024 21:49:25 -0400 Subject: [PATCH 1/2] Support Option --- .../Option.swift | 8 +- .../OptionTests.swift | 74 +++++---- crates/swift-bridge-ir/src/bridged_type.rs | 2 +- .../src/bridged_type/bridged_opaque_type.rs | 78 ++++++++-- .../src/codegen/codegen_tests.rs | 1 - ..._opaque_swift_type_return_codegen_tests.rs | 121 --------------- .../opaque_swift_type_codegen_tests.rs | 118 +++++++++++++++ .../codegen_tests/option_codegen_tests.rs | 143 +++++++++++++++++- .../src/async_function.rs | 2 + crates/swift-integration-tests/src/option.rs | 12 +- crates/swift-integration-tests/src/result.rs | 1 + 11 files changed, 384 insertions(+), 176 deletions(-) delete mode 100644 crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift index 50226d46..a3e9918e 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift @@ -63,6 +63,12 @@ func swift_arg_option_str(arg: Optional) -> Bool { } else { return false } - } +public class OptTestOpaqueSwiftType { + let val: Int + + init(val: Int) { + self.val = val + } +} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index 01b1d0e4..7a64f602 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -15,34 +15,34 @@ class OptionTests: XCTestCase { func testSwiftCallRustOptionPrimitive() throws { XCTAssertEqual(rust_reflect_option_u8(70), 70) XCTAssertEqual(rust_reflect_option_u8(nil), nil) - + XCTAssertEqual(rust_reflect_option_i8(70), 70) XCTAssertEqual(rust_reflect_option_i8(nil), nil) - + XCTAssertEqual(rust_reflect_option_u16(70), 70) XCTAssertEqual(rust_reflect_option_u16(nil), nil) - + XCTAssertEqual(rust_reflect_option_i16(70), 70) XCTAssertEqual(rust_reflect_option_i16(nil), nil) - + XCTAssertEqual(rust_reflect_option_u32(70), 70) XCTAssertEqual(rust_reflect_option_u32(nil), nil) - + XCTAssertEqual(rust_reflect_option_i32(70), 70) XCTAssertEqual(rust_reflect_option_i32(nil), nil) - + XCTAssertEqual(rust_reflect_option_u64(70), 70) XCTAssertEqual(rust_reflect_option_u64(nil), nil) - + XCTAssertEqual(rust_reflect_option_i64(70), 70) XCTAssertEqual(rust_reflect_option_i64(nil), nil) - + XCTAssertEqual(rust_reflect_option_f32(70.0), 70.0) XCTAssertEqual(rust_reflect_option_f32(nil), nil) - + XCTAssertEqual(rust_reflect_option_f64(70.0), 70.0) XCTAssertEqual(rust_reflect_option_f64(nil), nil) - + XCTAssertEqual(rust_reflect_option_bool(true), true) XCTAssertEqual(rust_reflect_option_bool(false), false) XCTAssertEqual(rust_reflect_option_bool(nil), nil) @@ -52,30 +52,30 @@ class OptionTests: XCTestCase { func testRustCallSwiftOptionPrimitive() throws { test_rust_calls_swift_option_primitive() } - + /// Verify that Swift can call a Rust function that accepts and returns an Option /// where T is a String. func testSwiftCallRustReturnOptionString() throws { let string = rust_reflect_option_string("hello world") XCTAssertEqual(string!.toString(), "hello world") - + let none: String? = nil XCTAssertNil(rust_reflect_option_string(none)) } - + /// We use an `Option<&'static str>` that we create on the Rust side so that /// we don't run into any lifetime issues. func testSwiftCallRustReturnOptionStr() throws { let str = rust_create_option_static_str() XCTAssertEqual(str!.toString(), "hello") - + let reflected = rust_reflect_option_str(str) XCTAssertEqual(reflected!.toString(), "hello") - + let none: RustStr? = nil XCTAssertNil(rust_reflect_option_str(none)) } - + func testSwiftCallRustWithOptionVecOfPrimitiveType() throws { let vec = RustVec() vec.push(value: 123) @@ -89,15 +89,24 @@ class OptionTests: XCTestCase { XCTAssertNil(rust_reflect_option_vector_rust_type(nil)) } - + func testSwiftCallRustWithOptionOpaqueRustType() throws { let val = OptTestOpaqueRustType(123) let reflect = rust_reflect_option_opaque_rust_type(val) XCTAssertEqual(reflect!.field(), 123) - + XCTAssertNil(rust_reflect_option_opaque_rust_type(nil)) } + /// Verify that we can bridge options of opaque Swift types. + func testSwiftCallRustWithOptionOpaqueSwiftType() throws { + let val = OptTestOpaqueSwiftType(val: 727) + let reflect = rust_reflect_option_opaque_swift_type(val) + XCTAssertEqual(reflect!.val, 727) + + XCTAssertNil(rust_reflect_option_opaque_swift_type(nil)) + } + /// Verify that we can pass and receive an `Option<&RustType>`. /// /// We deinitialize the first reference and create a second to confirm that @@ -110,39 +119,39 @@ class OptionTests: XCTestCase { XCTAssertEqual(reflect!.field(), 123) XCTAssertNil(rust_reflect_option_ref_opaque_rust_type(nil)) reflect = nil - + reflect = rust_reflect_option_ref_opaque_rust_type(opt_ref) XCTAssertEqual(reflect!.field(), 123) } - + func testSwiftCallRustWithOptionOpaqueRustCopyType() throws { let val = new_opaque_rust_copy_type(123) let _: OptTestOpaqueRustCopyType? = rust_reflect_option_opaque_rust_copy_type(val) - + // TODO: Support methods on generic types // XCTAssertEqual(reflect!.field(), 123) XCTAssertNil(rust_reflect_option_opaque_rust_copy_type(nil)) } - + func testSwiftCallRustWithOptionGenericOpaqueRustType() throws { let val = new_generic_opaque_rust_type(123) let _: OptTestGenericOpaqueRustType? = rust_reflect_option_generic_opaque_rust_type(val) - + // TODO: Support methods on generic types // XCTAssertEqual(reflect!.field(), 123) XCTAssertNil(rust_reflect_option_opaque_rust_type(nil)) } - + func testSwiftCallRustWithOptionGenericOpaqueRustCopyType() throws { let val = new_generic_opaque_rust_copy_type(123) let _: OptTestGenericOpaqueRustCopyType? = rust_reflect_option_generic_opaque_rust_copy_type(val) - + // TODO: Support methods on generic types // XCTAssertEqual(reflect!.field(), 123) XCTAssertNil(rust_reflect_option_generic_opaque_rust_copy_type(nil)) } - + func testStructWithOptionFieldsSome() throws { let val = StructWithOptionFields( u8: 123, i8: 123, u16: 123, i16: 123, @@ -165,7 +174,7 @@ class OptionTests: XCTestCase { XCTAssertEqual(reflected.f64, 123.4) XCTAssertEqual(reflected.boolean, true) } - + func testStructWithOptionFieldsNone() { let val = StructWithOptionFields( u8: nil, i8: nil, u16: nil, i16: nil, @@ -187,29 +196,28 @@ class OptionTests: XCTestCase { XCTAssertEqual(reflected.f64, nil) XCTAssertEqual(reflected.boolean, nil) } - + func testEnumWhereVariantsHaveNoData() { let val = OptionEnumWithNoData.Variant2 let reflectedSome = rust_reflect_option_enum_with_no_data(val) let reflectedNone = rust_reflect_option_enum_with_no_data(nil) - + switch reflectedSome! { case .Variant2: break; default: XCTFail() } - + XCTAssertNil(reflectedNone) } - + func testOptionStruct() { let val = OptionStruct(field: 123) let reflectedSome = rust_reflect_option_struct_with_no_data(val) let reflectedNone = rust_reflect_option_struct_with_no_data(nil) - + XCTAssertEqual(reflectedSome!.field, 123) XCTAssertNil(reflectedNone) } } - diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index 63eddc50..3e58309b 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -1499,7 +1499,7 @@ impl BridgedType { // // Say we have an extern Rust function `create_string(str: &str) -> String`. // It would be called using `__swift_bridge__$create_string(str)` - // But that would return a pointer to a swift_bridge::RustString.. So we need to convert that + // But that would return a pointer to a swift_bridge::RustString. So we need to convert that // to something Swift can make use of. // The final result on the Swift side would be: // diff --git a/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs b/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs index be01e057..0638c288 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs @@ -311,11 +311,24 @@ impl BridgeableType for OpaqueForeignType { } } } else { - quote! { - if let Some(val) = #expression { - Box::into_raw(Box::new(val)) - } else { - std::ptr::null_mut() + match self.host_lang { + HostLang::Rust => { + quote! { + if let Some(val) = #expression { + Box::into_raw(Box::new(val)) + } else { + std::ptr::null_mut() + } + } + } + HostLang::Swift => { + quote! { + if let Some(val) = #expression { + val.0.cast() + } else { + std::ptr::null_mut() + } + } } } } @@ -414,7 +427,14 @@ impl BridgeableType for OpaqueForeignType { expression = expression, ) } else { - format!("{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()", expression = expression,) + match self.host_lang { + HostLang::Rust => { + format!("{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()", expression = expression,) + } + HostLang::Swift => { + format!("{{ if let val = {expression} {{ return Unmanaged.passRetained(val).retain().toOpaque() }} else {{ return nil }} }}()") + } + } } } @@ -479,11 +499,28 @@ impl BridgeableType for OpaqueForeignType { } } } else { - quote! { - if #expression.is_null() { - None - } else { - Some(unsafe { * Box::from_raw(#expression) } ) + match self.host_lang { + HostLang::Rust => { + quote! { + if #expression.is_null() { + None + } else { + Some(unsafe { *Box::from_raw(#expression) } ) + } + } + } + HostLang::Swift => { + let ty = &self.ty; + quote! { + { + let val = #expression; + if val.is_null() { + None + } else { + Some(#ty(val.cast())) + } + } + } } } } @@ -545,11 +582,20 @@ impl BridgeableType for OpaqueForeignType { ) } else { let type_name = self.swift_name(); - format!( - "{{ let val = {expression}; if val != nil {{ return {type_name}(ptr: val!) }} else {{ return nil }} }}()", - expression = expression, - type_name = type_name - ) + match self.host_lang { + HostLang::Rust => { + format!( + "{{ let val = {expression}; if val != nil {{ return {type_name}(ptr: val!) }} else {{ return nil }} }}()", + expression = expression, + type_name = type_name + ) + } + HostLang::Swift => { + format!( + "{{ if let val = {expression} {{ return Unmanaged<{type_name}>.fromOpaque(val).takeRetainedValue() }} else {{ return nil }} }}()" + ) + } + } } } diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests.rs index 8e4e69e8..e68feaec 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests.rs @@ -39,7 +39,6 @@ mod derive_struct_attribute_codegen_tests; mod extern_rust_function_opaque_rust_type_argument_codegen_tests; mod extern_rust_function_opaque_rust_type_return_codegen_tests; mod extern_rust_method_swift_class_placement_codegen_tests; -mod extern_swift_function_opaque_swift_type_return_codegen_tests; mod function_attribute_codegen_tests; mod generic_opaque_rust_type_codegen_tests; mod opaque_rust_type_codegen_tests; diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs deleted file mode 100644 index 0a766ef0..00000000 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs +++ /dev/null @@ -1,121 +0,0 @@ -use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode}; -use proc_macro2::TokenStream; -use quote::quote; - -/// Verify that we generate the proper code for extern "Swift" functions that returns an -/// opaque Swift type. -mod test_extern_swift_freestanding_function_owned_opaque_swift_type_return { - use super::*; - - fn bridge_module_tokens() -> TokenStream { - quote! { - mod ffi { - extern "Swift" { - type SomeType; - - fn some_function() -> SomeType; - } - } - } - } - - fn expected_rust_tokens() -> ExpectedRustTokens { - ExpectedRustTokens::ContainsMany(vec![ - quote! { - pub fn some_function () -> SomeType { - unsafe { __swift_bridge__some_function() } - } - }, - quote! { - #[link_name = "__swift_bridge__$some_function"] - fn __swift_bridge__some_function() -> SomeType; - }, - ]) - } - - fn expected_swift_code() -> ExpectedSwiftCode { - ExpectedSwiftCode::ContainsAfterTrim( - r#" -@_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> UnsafeMutableRawPointer { - Unmanaged.passRetained(some_function()).toOpaque() -} -"#, - ) - } - - fn expected_c_header() -> ExpectedCHeader { - ExpectedCHeader::ExactAfterTrim("") - } - - #[test] - fn test_extern_swift_freestanding_function_owned_opaque_swift_type_return() { - 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 generate the proper code for extern "Swift" methods that returns an -/// opaque Swift type. -mod test_extern_swift_method_owned_opaque_swift_type_return { - use super::*; - - fn bridge_module_tokens() -> TokenStream { - quote! { - mod ffi { - extern "Swift" { - type SomeType; - - fn some_method(&self) -> SomeType; - } - } - } - } - - fn expected_rust_tokens() -> ExpectedRustTokens { - ExpectedRustTokens::ContainsMany(vec![ - quote! { - impl SomeType { - pub fn some_method (&self) -> SomeType { - unsafe { __swift_bridge__SomeType_some_method(swift_bridge::PointerToSwiftType(self.0)) } - } - } - }, - quote! { - #[link_name = "__swift_bridge__$SomeType$some_method"] - fn __swift_bridge__SomeType_some_method(this: swift_bridge::PointerToSwiftType) -> SomeType; - }, - ]) - } - - fn expected_swift_code() -> ExpectedSwiftCode { - ExpectedSwiftCode::ContainsAfterTrim( - r#" -@_cdecl("__swift_bridge__$SomeType$some_method") -func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { - Unmanaged.passRetained(Unmanaged.fromOpaque(this).takeUnretainedValue().some_method()).toOpaque() -} -"#, - ) - } - - fn expected_c_header() -> ExpectedCHeader { - ExpectedCHeader::ExactAfterTrim("") - } - - #[test] - fn test_extern_swift_method_owned_opaque_swift_type_return() { - 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/codegen_tests/opaque_swift_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs index ffc97fd5..a34048d1 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs @@ -65,3 +65,121 @@ func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { .test(); } } + +/// Verify that we generate the proper code for extern "Swift" functions that returns an +/// opaque Swift type. +mod test_extern_swift_freestanding_function_owned_opaque_swift_type_return { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + type SomeType; + + fn some_function() -> SomeType; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + pub fn some_function () -> SomeType { + unsafe { __swift_bridge__some_function() } + } + }, + quote! { + #[link_name = "__swift_bridge__$some_function"] + fn __swift_bridge__some_function() -> SomeType; + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$some_function") +func __swift_bridge__some_function () -> UnsafeMutableRawPointer { + Unmanaged.passRetained(some_function()).toOpaque() +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ExactAfterTrim("") + } + + #[test] + fn test_extern_swift_freestanding_function_owned_opaque_swift_type_return() { + 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 generate the proper code for extern "Swift" methods that returns an +/// opaque Swift type. +mod test_extern_swift_method_owned_opaque_swift_type_return { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + type SomeType; + + fn some_method(&self) -> SomeType; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + impl SomeType { + pub fn some_method (&self) -> SomeType { + unsafe { __swift_bridge__SomeType_some_method(swift_bridge::PointerToSwiftType(self.0)) } + } + } + }, + quote! { + #[link_name = "__swift_bridge__$SomeType$some_method"] + fn __swift_bridge__SomeType_some_method(this: swift_bridge::PointerToSwiftType) -> SomeType; + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$SomeType$some_method") +func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + Unmanaged.passRetained(Unmanaged.fromOpaque(this).takeUnretainedValue().some_method()).toOpaque() +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ExactAfterTrim("") + } + + #[test] + fn test_extern_swift_method_owned_opaque_swift_type_return() { + 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/codegen_tests/option_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs index fda0ccae..4a804fc5 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs @@ -533,7 +533,7 @@ func __swift_bridge__some_function () -> UnsafeMutableRawPointer? { } } -/// Test code generation for Swift function that accepts an Option argument. +/// Test code generation for Swift function that accepts an Option<&str> argument. mod extern_swift_func_option_str_arg { use super::*; @@ -652,6 +652,73 @@ void* __swift_bridge__$some_function(void); } } +/// Test code generation for Rust function that returns an Option +mod extern_rust_fn_return_option_opaque_swift_type { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + type SomeSwiftType; + } + + extern "Rust" { + fn some_function() -> Option; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + #[export_name = "__swift_bridge__$some_function"] + pub extern "C" fn __swift_bridge__some_function() -> *mut super::SomeSwiftType { + if let Some(val) = super::some_function() { + val.0.cast() + } else { + std::ptr::null_mut() + } + } + }, + quote! { + #[repr(C)] + pub struct SomeSwiftType(*mut std::ffi::c_void); + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +func some_function() -> Optional { + { if let val = __swift_bridge__$some_function() { return Unmanaged.fromOpaque(val).takeRetainedValue() } else { return nil } }() +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void* __swift_bridge__$some_function(void); + "#, + ) + } + + #[test] + fn extern_rust_fn_return_option_opaque_swift_type() { + 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 an Option<&OpaqueRustType> mod extern_rust_fn_return_option_ref_opaque_rust_type { use super::*; @@ -710,7 +777,7 @@ void* __swift_bridge__$some_function(void); } } -/// Test code generation for Rust function that returns an Option<&OpaqueRustType> +/// Test code generation for Rust function that takes an Option<&OpaqueRustType> argument mod extern_rust_fn_arg_option_ref_opaque_rust_type { use super::*; @@ -832,6 +899,78 @@ void __swift_bridge__$some_function(void* arg); } } +/// Test code generation for Rust function that takes an Option argument. +mod extern_rust_fn_with_option_opaque_swift_type_arg { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + type SomeSwiftType; + } + + extern "Rust" { + fn some_function(arg: Option); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + #[export_name = "__swift_bridge__$some_function"] + pub extern "C" fn __swift_bridge__some_function( + arg: *mut super::SomeSwiftType + ) { + super::some_function({ + let val = arg; + if val.is_null() { + None + } else { + Some(SomeSwiftType(val.cast())) + } + }) + } + }, + quote! { + #[repr(C)] + pub struct SomeSwiftType(*mut std::ffi::c_void); + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +func some_function(_ arg: Optional) { + __swift_bridge__$some_function({ if let val = arg { return Unmanaged.passRetained(val).retain().toOpaque() } else { return nil } }()) +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsAfterTrim( + r#" +void __swift_bridge__$some_function(void* arg); + "#, + ) + } + + #[test] + fn extern_rust_fn_with_option_opaque_swift_type_arg() { + 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 an Option> mod extern_rust_fn_return_option_generic_opaque_rust_type { use super::*; diff --git a/crates/swift-integration-tests/src/async_function.rs b/crates/swift-integration-tests/src/async_function.rs index 31e97ae7..3585ac6f 100644 --- a/crates/swift-integration-tests/src/async_function.rs +++ b/crates/swift-integration-tests/src/async_function.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + #[swift_bridge::bridge] mod ffi { #[swift_bridge(swift_repr = "struct")] diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index 3ae42603..acf81881 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -89,6 +89,9 @@ mod ffi { fn rust_reflect_option_opaque_rust_type( arg: Option, ) -> Option; + fn rust_reflect_option_opaque_swift_type( + arg: Option, + ) -> Option; fn rust_reflect_option_ref_opaque_rust_type( arg: Option<&OptTestOpaqueRustType>, @@ -122,6 +125,8 @@ mod ffi { } extern "Swift" { + type OptTestOpaqueSwiftType; + fn swift_reflect_option_u8(arg: Option) -> Option; fn swift_reflect_option_i8(arg: Option) -> Option; fn swift_reflect_option_u16(arg: Option) -> Option; @@ -264,7 +269,7 @@ mod reflect_primitives { pub fn rust_reflect_option_isize(arg: Option) -> Option { arg } pub fn rust_reflect_option_f32(arg: Option) -> Option { arg } pub fn rust_reflect_option_f64(arg: Option) -> Option { arg } - pub fn rust_reflect_option_bool(arg: Option) -> Option { arg } + pub fn rust_reflect_option_bool(arg: Option) -> Option { arg } } fn rust_reflect_option_string(arg: Option) -> Option { @@ -293,6 +298,11 @@ fn rust_reflect_option_ref_opaque_rust_type( ) -> Option<&OptTestOpaqueRustType> { arg } +pub fn rust_reflect_option_opaque_swift_type( + arg: Option, +) -> Option { + arg +} fn rust_reflect_option_opaque_rust_copy_type( arg: Option, diff --git a/crates/swift-integration-tests/src/result.rs b/crates/swift-integration-tests/src/result.rs index a460be30..1b2a776f 100644 --- a/crates/swift-integration-tests/src/result.rs +++ b/crates/swift-integration-tests/src/result.rs @@ -1,4 +1,5 @@ //! See also: crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs +#![allow(dead_code)] #[swift_bridge::bridge] mod ffi { From db71f4eb5429956c4ebadce6a11b26e7a1d0f6ff Mon Sep 17 00:00:00 2001 From: BrightShard Date: Sun, 28 Apr 2024 10:19:23 -0400 Subject: [PATCH 2/2] PR feedback & fix tests --- crates/swift-integration-tests/src/async_function.rs | 3 +++ crates/swift-integration-tests/src/option.rs | 6 ++++-- crates/swift-integration-tests/src/result.rs | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/swift-integration-tests/src/async_function.rs b/crates/swift-integration-tests/src/async_function.rs index 3585ac6f..d70a778b 100644 --- a/crates/swift-integration-tests/src/async_function.rs +++ b/crates/swift-integration-tests/src/async_function.rs @@ -1,3 +1,6 @@ +// This is a temporary workaround until https://github.com/chinedufn/swift-bridge/issues/270 +// is closed. When tests are compiled they have `-D warnings` (deny warnings) enabled, so +// tests won't even compile unless this warning is ignored. #![allow(dead_code)] #[swift_bridge::bridge] diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index acf81881..8a8bd4c4 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -1,5 +1,7 @@ //! See also: crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs +use ffi::OptTestOpaqueSwiftType; + #[swift_bridge::bridge] mod ffi { #[swift_bridge(swift_repr = "struct")] @@ -299,8 +301,8 @@ fn rust_reflect_option_ref_opaque_rust_type( arg } pub fn rust_reflect_option_opaque_swift_type( - arg: Option, -) -> Option { + arg: Option, +) -> Option { arg } diff --git a/crates/swift-integration-tests/src/result.rs b/crates/swift-integration-tests/src/result.rs index 1b2a776f..9941f058 100644 --- a/crates/swift-integration-tests/src/result.rs +++ b/crates/swift-integration-tests/src/result.rs @@ -1,4 +1,7 @@ //! See also: crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs +// This is a temporary workaround until https://github.com/chinedufn/swift-bridge/issues/270 +// is closed. When tests are compiled they have `-D warnings` (deny warnings) enabled, so +// tests won't even compile unless this warning is ignored. #![allow(dead_code)] #[swift_bridge::bridge]