Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift Vec<primitive> Return Type and Function Argument #229

Merged
merged 10 commits into from
Jun 20, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Foundation

func swift_arg_vec_u8(vec _: RustVec<UInt8>) {
assert(vec[0] == 1)
assert(vec[1] == 2)
assert(vec[2] == 3)
assert(vec[3] == 4)
assert(vec[4] == 5)
}

func swift_return_vec_u8() -> RustVec<UInt8> {
let vec = RustVec<UInt8>()
for i in 0 ... 4 {
vec.push(value: UInt8(i))
}
return vec
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class VecTests: XCTestCase {
XCTAssertEqual(RustVec<Float>().len(), 0);
XCTAssertEqual(RustVec<Double>().len(), 0);
}

/// Verify that Rust can pass `RustVec`s to and receive `RustVec`s from Swift.
func testRustCallsSwiftRustVecFunctions() {
run_vec_tests()
}
}


22 changes: 19 additions & 3 deletions crates/swift-bridge-ir/src/bridged_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1158,9 +1158,25 @@ impl BridgedType {
unimplemented!()
}
},
StdLibType::Vec(ty) => {
format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types))
}
StdLibType::Vec(ty) => match type_pos {
TypePosition::FnArg(func_host_lang, _) => {
if func_host_lang.is_rust() {
format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types))
} else {
"UnsafeMutableRawPointer".to_string()
}
}
TypePosition::FnReturn(func_host_lang) => {
if func_host_lang.is_rust() {
format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types))
} else {
"UnsafeMutableRawPointer".to_string()
}
}
_ => {
format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types))
}
},
StdLibType::Option(opt) => opt.to_swift_type(type_pos, types),
StdLibType::Result(result) => result.to_swift_type(type_pos, types),
StdLibType::BoxedFnOnce(boxed_fn) => boxed_fn.to_swift_type().to_string(),
Expand Down
113 changes: 113 additions & 0 deletions crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,116 @@ void __swift_bridge__$some_function(void* arg);
.test();
}
}

/// Test code generation for Rust function that returns a Vec<T> where T is a primitive Rust type.
mod extern_swift_fn_return_vec_of_primitive_rust_type {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
extern "Swift" {
fn some_function() -> Vec<u8>;
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
#[allow(non_snake_case)]
mod ffi {
pub fn some_function() -> Vec<u8> {
unsafe { *Box::from_raw(unsafe { __swift_bridge__some_function() }) }
}
extern "C" {
#[link_name = "__swift_bridge__$some_function"]
fn __swift_bridge__some_function() -> *mut Vec<u8>;
}
}
})
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
@_cdecl("__swift_bridge__$some_function")
func __swift_bridge__some_function () -> UnsafeMutableRawPointer {
{ let val = some_function(); val.isOwned = false; return val.ptr }()
}
"#,
)
}

fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::ExactAfterTrim("")
}

#[test]
fn extern_swift_fn_return_vec_of_primitive_rust_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 has an argument
/// Vec<T> where T is a primitive Rust type.
mod extern_swift_fn_arg_vec_of_primitive_rust_type {
use super::*;

fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
extern "Swift" {
fn some_function(arg: Vec<u8>);
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::Contains(quote! {
#[allow(non_snake_case)]
mod ffi {
pub fn some_function(arg: Vec<u8>) {
unsafe { __swift_bridge__some_function(Box::into_raw(Box::new(arg))) }
}
extern "C" {
#[link_name = "__swift_bridge__$some_function"]
fn __swift_bridge__some_function(arg: *mut Vec<u8>);
}
}
})
}

fn expected_swift_code() -> ExpectedSwiftCode {
ExpectedSwiftCode::ContainsAfterTrim(
r#"
@_cdecl("__swift_bridge__$some_function")
func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) {
some_function(arg: RustVec(ptr: arg))
}
"#,
)
}

fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::ExactAfterTrim("")
}

#[test]
fn extern_swift_fn_arg_vec_of_primitive_rust_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();
}
}
22 changes: 22 additions & 0 deletions crates/swift-integration-tests/src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ mod ffi {
arg: Vec<TransparentEnumInsideVecT>,
) -> Vec<TransparentEnumInsideVecT>;
}

extern "Rust" {
fn run_vec_tests();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to call this on the Swift side in VecTests.rs

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/chinedufn/swift-bridge/blob/master/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift#L11

something like:

/// Verify that Rust can pass `RustVec`s to and receive `RustVec`s from Swift.
func testRustCallsSwiftRustVecFunctions() {
    run_vec_tests()
}

}

extern "Swift" {
fn swift_return_vec_u8() -> Vec<u8>;
fn swift_arg_vec_u8(vec: Vec<u8>);
}
}

fn run_vec_tests() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

let vec = ffi::swift_return_vec_u8();
assert_eq!(vec.len(), 5);
assert_eq!(vec[0], 0);
assert_eq!(vec[1], 1);
assert_eq!(vec[2], 2);
assert_eq!(vec[3], 3);
assert_eq!(vec[4], 4);

let vec: Vec<u8> = vec![1, 2, 3, 4, 5];
ffi::swift_arg_vec_u8(vec);
}

pub struct ARustTypeInsideVecT {
Expand Down