From b179a777224e2ca61fd8bfced87240e5703253b8 Mon Sep 17 00:00:00 2001 From: niwaka Date: Sat, 28 Jan 2023 00:18:25 +0900 Subject: [PATCH 1/3] Support Option> where T is primitive --- .../OptionTests.swift | 12 ++++ crates/swift-bridge-ir/src/bridged_type.rs | 5 +- .../src/bridged_type/bridged_option.rs | 27 ++++++-- .../codegen_tests/option_codegen_tests.rs | 64 +++++++++++++++++++ crates/swift-integration-tests/src/option.rs | 7 +- 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index 66ec6654..ee6ce82f 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -94,6 +94,18 @@ class OptionTests: XCTestCase { XCTAssertNil(rust_reflect_option_str(none)) } + func testSwiftCallRustWithOptionVecType() throws { + let vec = RustVec() + vec.push(value: 123) + vec.push(value: 321) + let refrelct = rust_reflect_option_vector_rust_type(vec) + XCTAssertEqual(vec.len(), 2) + XCTAssertEqual(vec.get(index: 0), 123) + XCTAssertEqual(vec.get(index: 1), 321) + + XCTAssertNil(rust_reflect_option_vector_rust_type(nil)) + } + func testSwiftCallRustWithOptionOpaqueRustType() throws { let val = OptTestOpaqueRustType(123) let reflect = rust_reflect_option_opaque_rust_type(val) diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index 2bfd8eeb..edb89050 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -888,8 +888,9 @@ impl BridgedType { StdLibType::Str => { quote! { #swift_bridge_path::string::RustStr } } - StdLibType::Vec(_) => { - todo!("Option> is not yet supported") + StdLibType::Vec(ty) => { + let ty = ty.ty.to_rust_type_path(); + quote! { *mut Vec<#ty> } } StdLibType::Option(_) => { todo!("Option> is not yet supported") diff --git a/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs b/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs index 29c252c0..28e6ec58 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs @@ -90,7 +90,13 @@ impl BridgedOption { } } StdLibType::Vec(_) => { - todo!("Support Option>") + quote! { + if let Some(value) = #expression { + Box::into_raw(Box::new(value)) + } else { + std::ptr::null_mut() + } + } } StdLibType::Option(_) => { todo!("Support Option>") @@ -154,7 +160,9 @@ impl BridgedOption { } } StdLibType::Vec(_) => { - todo!("Option> is not yet supported") + quote! { + if #expression.is_null() { None } else { Some( unsafe { * Box::from_raw(#expression) } ) } + } } StdLibType::Option(_) => { todo!("Option> is not yet supported") @@ -214,7 +222,11 @@ impl BridgedOption { ) } StdLibType::Vec(_) => { - todo!("Support Option>") + format!( + "{{ let val = {expression}; if val != nil {{ return RustVec(ptr: val!) }} else {{ return nil }} }}()" + , + expression = expression + ) } StdLibType::Option(_) => { todo!("Support Option>") @@ -280,7 +292,10 @@ impl BridgedOption { format!("{expression}AsRustStr", expression = expression) } StdLibType::Vec(_) => { - todo!("Option is not yet supported") + format!( + "{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()" + , expression = expression + ) } StdLibType::Option(_) => { todo!("Option is not yet supported") @@ -340,9 +355,7 @@ impl BridgedOption { todo!("Option<&[T]> is not yet supported") } StdLibType::Str => "struct RustStr".to_string(), - StdLibType::Vec(_) => { - todo!("Option> is not yet supported") - } + StdLibType::Vec(_) => "void*".to_string(), StdLibType::Option(_) => { todo!("Option> is not yet supported") } 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 c2e555e9..4ae8ff34 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 @@ -4,6 +4,70 @@ use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode} use proc_macro2::TokenStream; use quote::quote; +/// Test code generation for Rust function that accepts and returns an Option> where T is a +/// primitive. +mod exterun_rust_fn_option_vector_primitive { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Rust" { + fn some_function (arg: Option>) -> Option>; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + #[export_name = "__swift_bridge__$some_function"] + pub extern "C" fn __swift_bridge__some_function( + arg: *mut Vec + ) -> *mut Vec { + if let Some(value) = super::some_function( + if arg.is_null() { + None + } else { + Some( unsafe { * Box::from_raw(arg) } ) + } + ) { + Box::into_raw(Box::new(value)) + } else { + std::ptr::null_mut() + } + } + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +public func some_function(_ arg: Optional>) -> Optional> { + { let val = __swift_bridge__$some_function({ if let val = arg { val.isOwned = false; return val.ptr } else { return nil } }()); if val != nil { return RustVec(ptr: val!) } else { return nil } }() +} +"#, + ) + } + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim( + r#" +void* __swift_bridge__$some_function(void* arg); + "#, + ); + + #[test] + fn exterun_rust_fn_option_vector_primitive() { + 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 accepts and returns an Option where T is a /// primitive. mod extern_rust_fn_option_primitive { diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index fbed961d..84a9801a 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -76,10 +76,11 @@ mod ffi { fn rust_reflect_option_bool(arg: Option) -> Option; fn rust_reflect_option_string(arg: Option) -> Option; - fn rust_create_option_static_str() -> Option<&'static str>; fn rust_reflect_option_str(arg: Option<&str>) -> Option<&str>; + fn rust_reflect_option_vector_rust_type(arg: Option>) -> Option>; + fn rust_reflect_option_opaque_rust_type( arg: Option, ) -> Option; @@ -192,6 +193,10 @@ fn rust_reflect_option_str(arg: Option<&str>) -> Option<&str> { arg } +fn rust_reflect_option_vector_rust_type(arg: Option>) -> Option> { + arg +} + fn rust_reflect_option_opaque_rust_type( arg: Option, ) -> Option { From 5785818c1ee5ab441e4d8737e72e6d34174dcfcb Mon Sep 17 00:00:00 2001 From: niwaka Date: Sat, 28 Jan 2023 08:02:23 +0900 Subject: [PATCH 2/3] Address review --- .../OptionTests.swift | 13 ++-- .../codegen_tests/option_codegen_tests.rs | 60 +++++++++---------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index ee6ce82f..586f7a9e 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -94,15 +94,16 @@ class OptionTests: XCTestCase { XCTAssertNil(rust_reflect_option_str(none)) } - func testSwiftCallRustWithOptionVecType() throws { + func testSwiftCallRustWithOptionVecOfPrimitiveType() throws { let vec = RustVec() vec.push(value: 123) vec.push(value: 321) - let refrelct = rust_reflect_option_vector_rust_type(vec) - XCTAssertEqual(vec.len(), 2) - XCTAssertEqual(vec.get(index: 0), 123) - XCTAssertEqual(vec.get(index: 1), 321) - + let reflected = rust_reflect_option_vector_rust_type(vec) + XCTAssertEqual(reflected.len(), 2) + + XCTAssertEqual(reflected.get(index: 0), 123) + XCTAssertEqual(reflected.get(index: 1), 321) + XCTAssertNil(rust_reflect_option_vector_rust_type(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 4ae8ff34..d4ee8fe0 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 @@ -4,16 +4,16 @@ use super::{CodegenTest, ExpectedCHeader, ExpectedRustTokens, ExpectedSwiftCode} use proc_macro2::TokenStream; use quote::quote; -/// Test code generation for Rust function that accepts and returns an Option> where T is a +/// Test code generation for Rust function that accepts and returns an Option where T is a /// primitive. -mod exterun_rust_fn_option_vector_primitive { +mod extern_rust_fn_option_primitive { use super::*; fn bridge_module_tokens() -> TokenStream { quote! { mod ffi { extern "Rust" { - fn some_function (arg: Option>) -> Option>; + fn some_function (arg: Option) -> Option; } } } @@ -23,18 +23,18 @@ mod exterun_rust_fn_option_vector_primitive { ExpectedRustTokens::Contains(quote! { #[export_name = "__swift_bridge__$some_function"] pub extern "C" fn __swift_bridge__some_function( - arg: *mut Vec - ) -> *mut Vec { - if let Some(value) = super::some_function( - if arg.is_null() { - None + arg: swift_bridge::option::OptionU8 + ) -> swift_bridge::option::OptionF32 { + if let Some(val) = super::some_function( + if arg.is_some { + Some(arg.val) } else { - Some( unsafe { * Box::from_raw(arg) } ) + None } ) { - Box::into_raw(Box::new(value)) + swift_bridge::option::OptionF32 { val, is_some: true} } else { - std::ptr::null_mut() + swift_bridge::option::OptionF32 { val: 123.4, is_some: false} } } }) @@ -43,8 +43,8 @@ mod exterun_rust_fn_option_vector_primitive { fn expected_swift_code() -> ExpectedSwiftCode { ExpectedSwiftCode::ContainsAfterTrim( r#" -public func some_function(_ arg: Optional>) -> Optional> { - { let val = __swift_bridge__$some_function({ if let val = arg { val.isOwned = false; return val.ptr } else { return nil } }()); if val != nil { return RustVec(ptr: val!) } else { return nil } }() +func some_function(_ arg: Optional) -> Optional { + { let val = __swift_bridge__$some_function({ let val = arg; return __private__OptionU8(val: val ?? 123, is_some: val != nil); }()); if val.is_some { return val.val } else { return nil } }() } "#, ) @@ -52,12 +52,12 @@ public func some_function(_ arg: Optional>) -> Optional where T is a +/// Test code generation for Rust function that accepts and returns an Option> where T is a /// primitive. -mod extern_rust_fn_option_primitive { +mod extern_rust_fn_option_vector_primitive { use super::*; fn bridge_module_tokens() -> TokenStream { quote! { mod ffi { extern "Rust" { - fn some_function (arg: Option) -> Option; + fn some_function (arg: Option>) -> Option>; } } } @@ -87,18 +87,18 @@ mod extern_rust_fn_option_primitive { ExpectedRustTokens::Contains(quote! { #[export_name = "__swift_bridge__$some_function"] pub extern "C" fn __swift_bridge__some_function( - arg: swift_bridge::option::OptionU8 - ) -> swift_bridge::option::OptionF32 { - if let Some(val) = super::some_function( - if arg.is_some { - Some(arg.val) - } else { + arg: *mut Vec + ) -> *mut Vec { + if let Some(value) = super::some_function( + if arg.is_null() { None + } else { + Some( unsafe { * Box::from_raw(arg) } ) } ) { - swift_bridge::option::OptionF32 { val, is_some: true} + Box::into_raw(Box::new(value)) } else { - swift_bridge::option::OptionF32 { val: 123.4, is_some: false} + std::ptr::null_mut() } } }) @@ -107,8 +107,8 @@ mod extern_rust_fn_option_primitive { fn expected_swift_code() -> ExpectedSwiftCode { ExpectedSwiftCode::ContainsAfterTrim( r#" -func some_function(_ arg: Optional) -> Optional { - { let val = __swift_bridge__$some_function({ let val = arg; return __private__OptionU8(val: val ?? 123, is_some: val != nil); }()); if val.is_some { return val.val } else { return nil } }() +public func some_function(_ arg: Optional>) -> Optional> { + { let val = __swift_bridge__$some_function({ if let val = arg { val.isOwned = false; return val.ptr } else { return nil } }()); if val != nil { return RustVec(ptr: val!) } else { return nil } }() } "#, ) @@ -116,12 +116,12 @@ func some_function(_ arg: Optional) -> Optional { const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim( r#" -struct __private__OptionF32 __swift_bridge__$some_function(struct __private__OptionU8 arg); +void* __swift_bridge__$some_function(void* arg); "#, ); #[test] - fn extern_rust_fn_option_primitive() { + fn extern_rust_fn_option_vector_primitive() { CodegenTest { bridge_module: bridge_module_tokens().into(), expected_rust_tokens: expected_rust_tokens(), From 0f664fac967e97384d6d49b4ff7dec492fd11d87 Mon Sep 17 00:00:00 2001 From: niwaka Date: Sat, 28 Jan 2023 08:09:23 +0900 Subject: [PATCH 3/3] Fix OptionTests.swift --- .../SwiftRustIntegrationTestRunnerTests/OptionTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift index 586f7a9e..d924d901 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/OptionTests.swift @@ -99,10 +99,10 @@ class OptionTests: XCTestCase { vec.push(value: 123) vec.push(value: 321) let reflected = rust_reflect_option_vector_rust_type(vec) - XCTAssertEqual(reflected.len(), 2) + XCTAssertEqual(reflected!.len(), 2) - XCTAssertEqual(reflected.get(index: 0), 123) - XCTAssertEqual(reflected.get(index: 1), 321) + XCTAssertEqual(reflected!.get(index: 0), 123) + XCTAssertEqual(reflected!.get(index: 1), 321) XCTAssertNil(rust_reflect_option_vector_rust_type(nil)) }