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 5a0649cf..ea979fa5 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 @@ -324,8 +324,18 @@ impl BridgeableType for OpaqueForeignType { HostLang::Swift => { let ty = &self.ty; + // Here we are converting a Swift type from its Rust representation to its FFI + // representation. + // When we drop the Rust representation we do not want to free the backing Swift + // type since we are passing ownership to Swift. + // So, we are transitioning this Swift type from its + // `RustRepr -> FfiRepr -> SwiftRepr`. + // This means that the Swift side will be responsible for freeing the Swift type + // whenever it is done with it. + // Here we use `ManuallyDrop` to avoid freeing the Swift type. quote! { if let Some(val) = #expression { + let val = std::mem::ManuallyDrop::new(val); val.0 as *mut super::#ty } else { std::ptr::null_mut() @@ -434,7 +444,7 @@ impl BridgeableType for OpaqueForeignType { 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 }} }}()") + format!("{{ if let val = {expression} {{ return Unmanaged.passRetained(val).toOpaque() }} else {{ return nil }} }}()") } } } 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 a23611af..9e1e20ed 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 @@ -676,6 +676,7 @@ mod extern_rust_fn_return_option_opaque_swift_type { #[export_name = "__swift_bridge__$some_function"] pub extern "C" fn __swift_bridge__some_function() -> *mut super::SomeSwiftType { if let Some(val) = super::some_function() { + let val = std::mem::ManuallyDrop::new(val); val.0 as *mut super::SomeSwiftType } else { std::ptr::null_mut() @@ -945,7 +946,7 @@ mod extern_rust_fn_with_option_opaque_swift_type_arg { 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 } }()) + __swift_bridge__$some_function({ if let val = arg { return Unmanaged.passRetained(val).toOpaque() } else { return nil } }()) } "#, )