Skip to content

Commit c1fd717

Browse files
authored
Support Vec<TransparentEnum> (#117)
This commit adds support for bridging `Vec<TransparentEnum>`. For example, the following is now possible: ```rust #[swift_bridge::bridge] mod ffi { enum SomeEnum { VariantA, VariantB, } extern "Rust" { fn some_function(arg: Vec<SomeEnum>) -> Vec<SomeEnum>; } } ```
1 parent 59bff34 commit c1fd717

File tree

16 files changed

+752
-200
lines changed

16 files changed

+752
-200
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ In addition to allowing you to share your own custom structs, enums and classes
121121
| &[T] | | Not yet implemented |
122122
| &mut [T] | | Not yet implemented |
123123
| Box<T> | | Not yet implemented |
124-
| Box<dyn FnOnce(A,B,C)> -> D> | (A, B, C) -> D | Passing from Rust to Swift is supported, but Swift to Rust is not yet implemented. |
125-
| Box<dyn Fn(A,B,C)> -> D> | (A, B, C) -> D | Not yet implemented |
124+
| Box<dyn FnOnce(A,B,C) -> D> | (A, B, C) -> D | Passing from Rust to Swift is supported, but Swift to Rust is not yet implemented. |
125+
| Box<dyn Fn(A,B,C) -> D> | (A, B, C) -> D | Not yet implemented |
126126
| [T; N] | | Not yet implemented |
127127
| *const T | UnsafePointer\<T> | |
128128
| *mut T | UnsafeMutablePointer\<T> | |

SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/VecTests.swift

+13
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class VecTests: XCTestCase {
6969
XCTAssertEqual(popped?.text().toString(), "hello world")
7070
XCTAssertEqual(vec.len(), 0)
7171
}
72+
7273
/// Verify that a Vec<T> of opaque Rust types can be used as an argument and return
7374
/// type for extern "Rust" functions.
7475
func testReflectVecOfOpaqueRustType() throws {
@@ -80,6 +81,18 @@ class VecTests: XCTestCase {
8081
XCTAssertEqual(reflected.get(index: 0)!.text().toString(), "hello world")
8182
}
8283

84+
/// Verify that a Vec<T> of transparent enums can be used as an argument and return
85+
/// type for extern "Rust" functions.
86+
func testReflectVecOfTransparentEnum() throws {
87+
let vec: RustVec<TransparentEnumInsideVecT> = RustVec()
88+
vec.push(value: TransparentEnumInsideVecT.VariantB)
89+
90+
let reflected = rust_reflect_vec_transparent_enum(vec)
91+
XCTAssertEqual(reflected.len(), 1)
92+
XCTAssertEqual(reflected.get(index: 0)!, TransparentEnumInsideVecT.VariantB)
93+
XCTAssertEqual(reflected.pop()!, TransparentEnumInsideVecT.VariantB)
94+
}
95+
8396
/// Verify that we can construct a RustVec of every primitive type.
8497
/// We tested all of the methods on two different primitives above to be sure that our
8598
/// functions that generate the pieces of the RustVec support aren't accidentally hard coded to

crates/swift-bridge-ir/src/bridged_type.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -767,9 +767,11 @@ impl BridgedType {
767767
#ty_name
768768
}
769769
}
770-
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(_shared_enum))) => {
771-
//
772-
todo!("Shared enum to Rust type name")
770+
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(shared_enum))) => {
771+
let enum_name = &shared_enum.name;
772+
quote! {
773+
#enum_name
774+
}
773775
}
774776
}
775777
}

crates/swift-bridge-ir/src/bridged_type/shared_enum.rs

+11
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ impl SharedEnum {
5252
}
5353
}
5454

55+
impl SharedEnum {
56+
/// Whether or not any of the enum's variants contain data.
57+
///
58+
/// `EnumWithData { VariantA(u8), VariantB }` -> true
59+
/// `EnumWithData { VariantA(u8), VariantB(u16) }` -> true
60+
/// `EnumWithNoData { VariantA, VariantB }` -> false
61+
pub fn has_one_or_more_variants_with_data(&self) -> bool {
62+
self.variants.iter().any(|v| !v.fields.is_empty())
63+
}
64+
}
65+
5566
impl PartialEq for SharedEnum {
5667
fn eq(&self, other: &Self) -> bool {
5768
self.name.to_string() == other.name.to_string() && self.variants == other.variants

crates/swift-bridge-ir/src/codegen/codegen_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ mod opaque_rust_type_codegen_tests;
4141
mod opaque_swift_type_codegen_tests;
4242
mod option_codegen_tests;
4343
mod result_codegen_tests;
44-
mod shared_enum_codegen_tests;
45-
mod shared_struct_codegen_tests;
4644
mod string_codegen_tests;
45+
mod transparent_enum_codegen_tests;
46+
mod transparent_struct_codegen_tests;
4747
mod vec_codegen_tests;
4848

4949
struct CodegenTest {

crates/swift-bridge-ir/src/codegen/codegen_tests/shared_enum_codegen_tests.rs crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod generates_enum_to_and_from_ffi_conversions_no_data {
2121

2222
fn expected_rust_tokens() -> ExpectedRustTokens {
2323
ExpectedRustTokens::Contains(quote! {
24+
#[derive(Copy, Clone)]
2425
pub enum SomeEnum {
2526
Variant1,
2627
Variant2
@@ -95,7 +96,7 @@ extension __swift_bridge__$SomeEnum {
9596
}
9697

9798
fn expected_c_header() -> ExpectedCHeader {
98-
ExpectedCHeader::ExactAfterTrim(
99+
ExpectedCHeader::ContainsAfterTrim(
99100
r#"
100101
#include <stdbool.h>
101102
typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$Variant1, __swift_bridge__$SomeEnum$Variant2, } __swift_bridge__$SomeEnumTag;
@@ -274,15 +275,17 @@ func some_function(_ arg: Optional<SomeEnum>) -> Optional<SomeEnum> {
274275
}
275276

276277
fn expected_c_header() -> ExpectedCHeader {
277-
ExpectedCHeader::ExactAfterTrim(
278+
ExpectedCHeader::ContainsManyAfterTrim(vec![
278279
r#"
279280
#include <stdbool.h>
280281
typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$Variant1, __swift_bridge__$SomeEnum$Variant2, } __swift_bridge__$SomeEnumTag;
281282
typedef struct __swift_bridge__$SomeEnum { __swift_bridge__$SomeEnumTag tag; } __swift_bridge__$SomeEnum;
282283
typedef struct __swift_bridge__$Option$SomeEnum { bool is_some; __swift_bridge__$SomeEnum val; } __swift_bridge__$Option$SomeEnum;
284+
"#,
285+
r#"
283286
struct __swift_bridge__$Option$SomeEnum __swift_bridge__$some_function(struct __swift_bridge__$Option$SomeEnum arg);
284287
"#,
285-
)
288+
])
286289
}
287290

288291
#[test]

crates/swift-bridge-ir/src/codegen/codegen_tests/vec_codegen_tests.rs

+260-2
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ void* __swift_bridge__$Vec_MyRustType$as_ptr(void* vec_ptr);
159159
}
160160

161161
#[test]
162-
fn extern_rust_fn_return_vec_of_opaque_rust_type() {
162+
fn extern_rust_type_vec_support() {
163163
CodegenTest {
164164
bridge_module: bridge_module_tokens().into(),
165165
expected_rust_tokens: expected_rust_tokens(),
@@ -268,7 +268,265 @@ void __swift_bridge__$some_function(void* arg);
268268
}
269269

270270
#[test]
271-
fn extern_rust_fn_return_vec_of_opaque_rust_type() {
271+
fn extern_rust_fn_arg_vec_of_opaque_rust_type() {
272+
CodegenTest {
273+
bridge_module: bridge_module_tokens().into(),
274+
expected_rust_tokens: expected_rust_tokens(),
275+
expected_swift_code: expected_swift_code(),
276+
expected_c_header: expected_c_header(),
277+
}
278+
.test();
279+
}
280+
}
281+
282+
/// Verify that we emit Rust, Swift and C header code that allows a transparent enum be used
283+
/// within a Vec<T>.
284+
mod transparent_enum_vec_support {
285+
use super::*;
286+
287+
fn bridge_module_tokens() -> TokenStream {
288+
quote! {
289+
mod ffi {
290+
enum SomeEnum {
291+
VariantA,
292+
VariantB
293+
}
294+
}
295+
}
296+
}
297+
298+
fn expected_rust_tokens() -> ExpectedRustTokens {
299+
ExpectedRustTokens::Contains(quote! {
300+
const _: () = {
301+
#[doc(hidden)]
302+
#[export_name = "__swift_bridge__$Vec_SomeEnum$new"]
303+
pub extern "C" fn _new() -> *mut Vec<SomeEnum> {
304+
Box::into_raw(Box::new(Vec::new()))
305+
}
306+
307+
#[doc(hidden)]
308+
#[export_name = "__swift_bridge__$Vec_SomeEnum$drop"]
309+
pub extern "C" fn _drop(vec: *mut Vec<SomeEnum>) {
310+
let vec = unsafe { Box::from_raw(vec) };
311+
drop(vec)
312+
}
313+
314+
#[doc(hidden)]
315+
#[export_name = "__swift_bridge__$Vec_SomeEnum$len"]
316+
pub extern "C" fn _len(vec: *const Vec<SomeEnum>) -> usize {
317+
unsafe { &*vec }.len()
318+
}
319+
320+
#[doc(hidden)]
321+
#[export_name = "__swift_bridge__$Vec_SomeEnum$get"]
322+
pub extern "C" fn _get(vec: *const Vec<SomeEnum>, index: usize) -> __swift_bridge__Option_SomeEnum {
323+
let vec = unsafe { &*vec };
324+
let val = vec.get(index).map(|v| *v);
325+
__swift_bridge__Option_SomeEnum::from_rust_repr(val)
326+
}
327+
328+
#[doc(hidden)]
329+
#[export_name = "__swift_bridge__$Vec_SomeEnum$get_mut"]
330+
pub extern "C" fn _get_mut(vec: *mut Vec<SomeEnum>, index: usize) -> __swift_bridge__Option_SomeEnum {
331+
let vec = unsafe { &mut *vec };
332+
let val = vec.get_mut(index).map(|v| *v);
333+
__swift_bridge__Option_SomeEnum::from_rust_repr(val)
334+
}
335+
336+
#[doc(hidden)]
337+
#[export_name = "__swift_bridge__$Vec_SomeEnum$push"]
338+
pub extern "C" fn _push(vec: *mut Vec<SomeEnum>, val: __swift_bridge__SomeEnum) {
339+
unsafe { &mut *vec }.push(val.into_rust_repr())
340+
}
341+
342+
#[doc(hidden)]
343+
#[export_name = "__swift_bridge__$Vec_SomeEnum$pop"]
344+
pub extern "C" fn _pop(vec: *mut Vec<SomeEnum>) -> __swift_bridge__Option_SomeEnum {
345+
let vec = unsafe { &mut *vec };
346+
let val = vec.pop();
347+
__swift_bridge__Option_SomeEnum::from_rust_repr(val)
348+
}
349+
350+
#[doc(hidden)]
351+
#[export_name = "__swift_bridge__$Vec_SomeEnum$as_ptr"]
352+
pub extern "C" fn _as_ptr(vec: *const Vec<SomeEnum>) -> *const SomeEnum {
353+
unsafe { & *vec }.as_ptr()
354+
}
355+
};
356+
})
357+
}
358+
359+
fn expected_swift_code() -> ExpectedSwiftCode {
360+
ExpectedSwiftCode::ContainsAfterTrim(
361+
r#"
362+
extension SomeEnum: Vectorizable {
363+
public static func vecOfSelfNew() -> UnsafeMutableRawPointer {
364+
__swift_bridge__$Vec_SomeEnum$new()
365+
}
366+
367+
public static func vecOfSelfFree(vecPtr: UnsafeMutableRawPointer) {
368+
__swift_bridge__$Vec_SomeEnum$drop(vecPtr)
369+
}
370+
371+
public static func vecOfSelfPush(vecPtr: UnsafeMutableRawPointer, value: Self) {
372+
__swift_bridge__$Vec_SomeEnum$push(vecPtr, value.intoFfiRepr())
373+
}
374+
375+
public static func vecOfSelfPop(vecPtr: UnsafeMutableRawPointer) -> Optional<Self> {
376+
let maybeEnum = __swift_bridge__$Vec_SomeEnum$pop(vecPtr)
377+
return maybeEnum.intoSwiftRepr()
378+
}
379+
380+
public static func vecOfSelfGet(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional<Self> {
381+
let maybeEnum = __swift_bridge__$Vec_SomeEnum$get(vecPtr, index)
382+
return maybeEnum.intoSwiftRepr()
383+
}
384+
385+
public static func vecOfSelfGetMut(vecPtr: UnsafeMutableRawPointer, index: UInt) -> Optional<Self> {
386+
let maybeEnum = __swift_bridge__$Vec_SomeEnum$get_mut(vecPtr, index)
387+
return maybeEnum.intoSwiftRepr()
388+
}
389+
390+
public static func vecOfSelfLen(vecPtr: UnsafeMutableRawPointer) -> UInt {
391+
__swift_bridge__$Vec_SomeEnum$len(vecPtr)
392+
}
393+
}
394+
"#,
395+
)
396+
}
397+
398+
fn expected_c_header() -> ExpectedCHeader {
399+
ExpectedCHeader::ContainsAfterTrim(
400+
r#"
401+
void* __swift_bridge__$Vec_SomeEnum$new(void);
402+
void __swift_bridge__$Vec_SomeEnum$drop(void* vec_ptr);
403+
void __swift_bridge__$Vec_SomeEnum$push(void* vec_ptr, __swift_bridge__$SomeEnum item);
404+
__swift_bridge__$Option$SomeEnum __swift_bridge__$Vec_SomeEnum$pop(void* vec_ptr);
405+
__swift_bridge__$Option$SomeEnum __swift_bridge__$Vec_SomeEnum$get(void* vec_ptr, uintptr_t index);
406+
__swift_bridge__$Option$SomeEnum __swift_bridge__$Vec_SomeEnum$get_mut(void* vec_ptr, uintptr_t index);
407+
uintptr_t __swift_bridge__$Vec_SomeEnum$len(void* vec_ptr);
408+
void* __swift_bridge__$Vec_SomeEnum$as_ptr(void* vec_ptr);
409+
"#,
410+
)
411+
}
412+
413+
#[test]
414+
fn transparent_enum_vec_support() {
415+
CodegenTest {
416+
bridge_module: bridge_module_tokens().into(),
417+
expected_rust_tokens: expected_rust_tokens(),
418+
expected_swift_code: expected_swift_code(),
419+
expected_c_header: expected_c_header(),
420+
}
421+
.test();
422+
}
423+
}
424+
425+
/// Test code generation for Rust function that returns a Vec<T> where T is a transparent enum.
426+
mod extern_rust_fn_return_vec_of_transparent_enum {
427+
use super::*;
428+
429+
fn bridge_module_tokens() -> TokenStream {
430+
quote! {
431+
mod ffi {
432+
enum SomeEnum {
433+
A
434+
}
435+
436+
extern "Rust" {
437+
fn some_function() -> Vec<SomeEnum>;
438+
}
439+
}
440+
}
441+
}
442+
443+
fn expected_rust_tokens() -> ExpectedRustTokens {
444+
ExpectedRustTokens::Contains(quote! {
445+
pub extern "C" fn __swift_bridge__some_function() -> *mut Vec<SomeEnum> {
446+
Box::into_raw(Box::new(super::some_function()))
447+
}
448+
})
449+
}
450+
451+
fn expected_swift_code() -> ExpectedSwiftCode {
452+
ExpectedSwiftCode::ContainsAfterTrim(
453+
r#"
454+
func some_function() -> RustVec<SomeEnum> {
455+
RustVec(ptr: __swift_bridge__$some_function())
456+
}
457+
"#,
458+
)
459+
}
460+
461+
fn expected_c_header() -> ExpectedCHeader {
462+
ExpectedCHeader::ContainsAfterTrim(
463+
r#"
464+
void* __swift_bridge__$some_function(void);
465+
"#,
466+
)
467+
}
468+
469+
#[test]
470+
fn extern_rust_fn_return_vec_of_transparent_enum() {
471+
CodegenTest {
472+
bridge_module: bridge_module_tokens().into(),
473+
expected_rust_tokens: expected_rust_tokens(),
474+
expected_swift_code: expected_swift_code(),
475+
expected_c_header: expected_c_header(),
476+
}
477+
.test();
478+
}
479+
}
480+
481+
/// Test code generation for Rust function that has an argument
482+
/// Vec<T> where T is a transparent enum.
483+
mod extern_rust_fn_arg_vec_of_transparent_enum {
484+
use super::*;
485+
486+
fn bridge_module_tokens() -> TokenStream {
487+
quote! {
488+
mod ffi {
489+
enum SomeEnum {
490+
A
491+
}
492+
extern "Rust" {
493+
type MyRustType;
494+
fn some_function(arg: Vec<SomeEnum>);
495+
}
496+
}
497+
}
498+
}
499+
500+
fn expected_rust_tokens() -> ExpectedRustTokens {
501+
ExpectedRustTokens::Contains(quote! {
502+
pub extern "C" fn __swift_bridge__some_function(
503+
arg: *mut Vec<SomeEnum>
504+
) {
505+
super::some_function(unsafe { * Box::from_raw(arg) })
506+
}
507+
})
508+
}
509+
510+
fn expected_swift_code() -> ExpectedSwiftCode {
511+
ExpectedSwiftCode::ContainsAfterTrim(
512+
r#"
513+
func some_function(_ arg: RustVec<SomeEnum>) {
514+
__swift_bridge__$some_function({ let val = arg; val.isOwned = false; return val.ptr }())
515+
}
516+
"#,
517+
)
518+
}
519+
520+
fn expected_c_header() -> ExpectedCHeader {
521+
ExpectedCHeader::ContainsAfterTrim(
522+
r#"
523+
void __swift_bridge__$some_function(void* arg);
524+
"#,
525+
)
526+
}
527+
528+
#[test]
529+
fn extern_rust_fn_arg_vec_of_transparent_enum() {
272530
CodegenTest {
273531
bridge_module: bridge_module_tokens().into(),
274532
expected_rust_tokens: expected_rust_tokens(),

0 commit comments

Comments
 (0)