Skip to content

Commit

Permalink
Support opaque types as enum variant fields (#181)
Browse files Browse the repository at this point in the history
This commit adds support for opaque types as enum variant fields. However, This PR doesn't support generic opaque types. I'll write the reason later.

Here's an example:

```rust
#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        #[swift_bridge(Equatable)]
        type Foo;
        #[swift_bridge(associated_to = Foo)]
        fn new() -> Foo;
    }

    enum EnumWithNamedData {
        Variant1 { hello: String, data_u8: u8 },
        Variant2 { data_i32: i32 },
        Variant3 { foo: Foo },
    }

    extern "Rust" {
        fn reflect_enum_with_named_data(arg: EnumWithNamedData) -> EnumWithNamedData;
    }
}
```

```Swift
let enumWithNamedData3 = EnumWithNamedData.Variant3(foo: Foo.new())
switch reflect_enum_with_named_data(enumWithNamedData3) {
case .Variant3(let foo):
    XCTAssertEqual(foo, Foo.new())
    break
default:
    XCTFail()
}
```
  • Loading branch information
NiwakaDev authored Feb 28, 2023
1 parent 27d7be9 commit 832498a
Show file tree
Hide file tree
Showing 15 changed files with 593 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,58 +33,99 @@ class SharedEnumTests: XCTestCase {
}

func testEnumWithUnnamedData() {
let enumWithUnnamedData1 = EnumWithUnnamedData.Variant1(create_string("hello"), 0)
let enumWithUnnamedData1 = EnumWithUnnamedData.TwoFields(create_string("hello"), OpaqueRustForEnumTest())
switch reflect_enum_with_unnamed_data(enumWithUnnamedData1) {
case .Variant1(let rustString, let valueUInt32):
case .TwoFields(let rustString, let opaqueRustForEnumTest):
XCTAssertEqual(rustString.toString(), "hello")
XCTAssertEqual(valueUInt32, 0)
XCTAssertEqual(opaqueRustForEnumTest, OpaqueRustForEnumTest())
default:
XCTFail()
}

let enumWithUnnamedData2 = EnumWithUnnamedData.Variant2(1000, 10)
let enumWithUnnamedData2 = EnumWithUnnamedData.OneField(1000)
switch reflect_enum_with_unnamed_data(enumWithUnnamedData2) {
case .Variant2(let valueInt32, let valueUInt8):
case .OneField(let valueInt32):
XCTAssertEqual(valueInt32, 1000)
XCTAssertEqual(valueUInt8, 10)
default:
XCTFail()
}

let enumWithUnnamedData3 = EnumWithUnnamedData.Variant3
let enumWithUnnamedData3 = EnumWithUnnamedData.NoFields
switch reflect_enum_with_unnamed_data(enumWithUnnamedData3) {
case .Variant3:
case .NoFields:
break
default:
XCTFail()
}
}

func testEnumWithNamedData() {
let enumWithNamedData1 = EnumWithNamedData.Variant1(hello: create_string("hello"), data_u8: 123)
let enumWithNamedData1 = EnumWithNamedData.TwoFields(hello: create_string("hello"), data_u8: 123)
switch reflect_enum_with_named_data(enumWithNamedData1) {
case .Variant1(let hello, let dataU8):
case .TwoFields(let hello, let dataU8):
XCTAssertEqual(hello.toString(), "hello")
XCTAssertEqual(dataU8, 123)
default:
XCTFail()
}

let enumWithNamedData2 = EnumWithNamedData.Variant2(data_i32: -123)
let enumWithNamedData2 = EnumWithNamedData.OneField(data_i32: -123)
switch reflect_enum_with_named_data(enumWithNamedData2) {
case .Variant2(let dataI32):
case .OneField(let dataI32):
XCTAssertEqual(dataI32, -123)
default:
XCTFail()
}

let enumWithNamedData3 = EnumWithNamedData.Variant3
let enumWithNamedData3 = EnumWithNamedData.NoFields
switch reflect_enum_with_named_data(enumWithNamedData3) {
case .Variant3:
case .NoFields:
break
default:
XCTFail()
}
}

func testEnumWithOpaqueRust() {
let named = EnumWithOpaqueRust.Named(data: OpaqueRustForEnumTest())
switch reflect_enum_with_opaque_type(named) {
case .Named(let value):
XCTAssertEqual(value, OpaqueRustForEnumTest())
case .Unnamed(_):
XCTFail()
}

let unnamed = EnumWithOpaqueRust.Unnamed(OpaqueRustForEnumTest())
switch reflect_enum_with_opaque_type(unnamed) {
case .Named(_):
XCTFail()
case .Unnamed(let value):
XCTAssertEqual(value, OpaqueRustForEnumTest())
}
}

func testEnumWithGenericOpaqueRust() {
let named = EnumWithGenericOpaqueRust.Named(data: new_generic_opaque_rust_for_enum_test())
switch reflect_enum_with_generic_opaque_type(named) {
case .Named(_):
// TODO: call a method on GenericOpaqueRustForEnumTest<Int32>
// after we add support for methods on generic opaque Rust Types.
// See https://github.com/chinedufn/swift-bridge/issues/44#issuecomment-1114198605
break
case .Unnamed(_):
XCTFail()
}

let unnamed = EnumWithGenericOpaqueRust.Unnamed(new_generic_opaque_rust_for_enum_test())
switch reflect_enum_with_generic_opaque_type(unnamed) {
case .Named(_):
XCTFail()
case .Unnamed(_):
// TODO: call a method on GenericOpaqueRustForEnumTest<Int32>
// after we add support for methods on generic opaque Rust Types.
// See https://github.com/chinedufn/swift-bridge/issues/44#issuecomment-1114198605
break
}
}

}
154 changes: 49 additions & 105 deletions crates/swift-bridge-ir/src/bridged_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub(crate) trait BridgeableType: Debug {

/// Get the Rust representation of this type.
/// For a string this might be `std::string::String`.
fn to_rust_type_path(&self) -> TokenStream;
fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream;

/// Get the Swift representation of this type.
///
Expand Down Expand Up @@ -440,25 +440,8 @@ impl BridgeableType for BridgedType {
}
}

/***
fn extract_swift_result_variants(
&self,
type_pos: TypePosition,
types: &TypeDeclarations,
) -> Option<(String, String)> {
match self {
BridgedType::StdLib(StdLibType::Result(result)) => Some((
result.ok_ty.to_swift_type(type_pos, types),
result.err_ty.to_swift_type(type_pos, types),
)),
BridgedType::Bridgeable(ty) => ty.extract_swift_result_variants(type_pos, types),
_ => None,
}
}
***/

fn to_rust_type_path(&self) -> TokenStream {
self.to_rust_type_path()
fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream {
self.to_rust_type_path(types)
}

fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String {
Expand Down Expand Up @@ -783,58 +766,43 @@ impl BridgedType {
// U8 -> u8
// Vec<U32> -> Vec<u32>
// SomeOpaqueRustType -> super::SomeOpaqueRustType
pub(crate) fn to_rust_type_path(&self) -> TokenStream {
pub(crate) fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream {
match self {
BridgedType::Bridgeable(b) => b.to_rust_type_path(),
BridgedType::StdLib(stdlib_type) => {
match stdlib_type {
StdLibType::Null => {
quote! {()}
}
StdLibType::U8 => quote! { u8 },
StdLibType::I8 => quote! { i8 },
StdLibType::U16 => quote! { u16},
StdLibType::I16 => quote! { i16},
StdLibType::U32 => quote! { u32 },
StdLibType::I32 => quote! { i32 },
StdLibType::U64 => quote! { u64 },
StdLibType::I64 => quote! { i64 },
StdLibType::Usize => quote! { usize },
StdLibType::Isize => quote! { isize },
StdLibType::F32 => quote! { f32 },
StdLibType::F64 => quote! { f64 },
StdLibType::Bool => quote! { bool },
StdLibType::Pointer(ptr) => {
let ptr_kind = &ptr.kind;

match &ptr.pointee {
Pointee::BuiltIn(ty) => {
let ty = ty.to_rust_type_path();
quote! { #ptr_kind #ty}
}
Pointee::Void(_ty) => {
// quote! { * #ptr_kind #ty };
panic!("Add a test case that hits this branch, then make it pass")
}
}
}
StdLibType::RefSlice(ref_slice) => {
let ty = ref_slice.ty.to_rust_type_path();
quote! { &[#ty]}
}
StdLibType::Str => quote! { &str },
StdLibType::Vec(v) => {
let ty = v.ty.to_rust_type_path();
quote! { Vec<#ty> }
}
StdLibType::Option(opt) => {
let ty = opt.ty.to_rust_type_path();
quote! { Option<#ty> }
}
StdLibType::Result(result) => result.to_rust_type_path(),
StdLibType::BoxedFnOnce(fn_once) => fn_once.to_rust_type_path(),
BridgedType::Bridgeable(b) => b.to_rust_type_path(types),
BridgedType::StdLib(stdlib_type) => match stdlib_type {
StdLibType::Null => {
quote! {()}
}
}
StdLibType::U8 => quote! { u8 },
StdLibType::I8 => quote! { i8 },
StdLibType::U16 => quote! { u16},
StdLibType::I16 => quote! { i16},
StdLibType::U32 => quote! { u32 },
StdLibType::I32 => quote! { i32 },
StdLibType::U64 => quote! { u64 },
StdLibType::I64 => quote! { i64 },
StdLibType::Usize => quote! { usize },
StdLibType::Isize => quote! { isize },
StdLibType::F32 => quote! { f32 },
StdLibType::F64 => quote! { f64 },
StdLibType::Bool => quote! { bool },
StdLibType::Pointer(ptr) => ptr.to_rust_type_path(types),
StdLibType::RefSlice(ref_slice) => {
let ty = ref_slice.ty.to_rust_type_path(types);
quote! { &[#ty]}
}
StdLibType::Str => quote! { &str },
StdLibType::Vec(v) => {
let ty = v.ty.to_rust_type_path(types);
quote! { Vec<#ty> }
}
StdLibType::Option(opt) => {
let ty = opt.ty.to_rust_type_path(types);
quote! { Option<#ty> }
}
StdLibType::Result(result) => result.to_rust_type_path(types),
StdLibType::BoxedFnOnce(fn_once) => fn_once.to_rust_type_path(types),
},
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => {
let ty_name = &shared_struct.name;
quote! {
Expand Down Expand Up @@ -877,18 +845,7 @@ impl BridgedType {
StdLibType::Isize => quote! { isize },
StdLibType::Bool => quote! { bool },
StdLibType::Pointer(ptr) => {
let kind = ptr.kind.to_token_stream();

let ty = match &ptr.pointee {
Pointee::BuiltIn(ty) => {
ty.to_ffi_compatible_rust_type(swift_bridge_path, types)
}
Pointee::Void(ty) => {
quote! { super::#ty }
}
};

quote! { #kind #ty}
ptr.to_ffi_compatible_rust_type(swift_bridge_path, types)
}
StdLibType::RefSlice(slice) => {
let ty = slice
Expand All @@ -903,7 +860,7 @@ impl BridgedType {
quote! { () }
}
StdLibType::Vec(ty) => {
let ty = ty.ty.to_rust_type_path();
let ty = ty.ty.to_rust_type_path(types);
quote! { *mut Vec<#ty> }
}
StdLibType::Option(opt) => match opt.ty.deref() {
Expand Down Expand Up @@ -963,7 +920,7 @@ impl BridgedType {
quote! { #swift_bridge_path::string::RustStr }
}
StdLibType::Vec(ty) => {
let ty = ty.ty.to_rust_type_path();
let ty = ty.ty.to_rust_type_path(types);
quote! { *mut Vec<#ty> }
}
StdLibType::Option(_) => {
Expand All @@ -990,7 +947,7 @@ impl BridgedType {
}
},
StdLibType::Result(result) => result.to_ffi_compatible_rust_type(swift_bridge_path),
StdLibType::BoxedFnOnce(fn_once) => fn_once.to_ffi_compatible_rust_type(),
StdLibType::BoxedFnOnce(fn_once) => fn_once.to_ffi_compatible_rust_type(types),
},
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => {
let ty_name = &shared_struct.name;
Expand Down Expand Up @@ -1223,26 +1180,13 @@ impl BridgedType {
/// unsafe { __swift_bridge__void_pointers(arg1) }
/// }
///
pub fn maybe_convert_pointer_to_super_pointer(&self) -> TokenStream {
pub fn maybe_convert_pointer_to_super_pointer(&self, types: &TypeDeclarations) -> TokenStream {
match self {
BridgedType::StdLib(stdlib_type) => {
match stdlib_type {
StdLibType::Pointer(pointer) => match &pointer.pointee {
Pointee::BuiltIn(_built_in) => {
//
self.to_rust_type_path()
}
Pointee::Void(_) => {
let pointer_kind = &pointer.kind;
let pointee = &pointer.pointee;

quote! { #pointer_kind super:: #pointee }
}
},
_ => self.to_rust_type_path(),
}
}
_ => self.to_rust_type_path(),
BridgedType::StdLib(stdlib_type) => match stdlib_type {
StdLibType::Pointer(pointer) => pointer.to_rust_type_path(types),
_ => self.to_rust_type_path(types),
},
_ => self.to_rust_type_path(types),
}
}

Expand Down Expand Up @@ -1307,7 +1251,7 @@ impl BridgedType {
span,
),
StdLibType::BoxedFnOnce(fn_once) => {
fn_once.convert_rust_value_to_ffi_compatible_value(expression)
fn_once.convert_rust_value_to_ffi_compatible_value(expression, types)
}
},
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => {
Expand Down
29 changes: 21 additions & 8 deletions crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ impl BridgeableBoxedFnOnce {
}

/// Box<dyn FnOnce(A, B) -> C>
pub fn to_rust_type_path(&self) -> TokenStream {
let args: Vec<TokenStream> = self.params.iter().map(|a| a.to_rust_type_path()).collect();
let ret = &self.ret.to_rust_type_path();
pub fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream {
let args: Vec<TokenStream> = self
.params
.iter()
.map(|a| a.to_rust_type_path(types))
.collect();
let ret = &self.ret.to_rust_type_path(types);
quote! {
Box<dyn FnOnce(#(#args),*) -> #ret>
}
Expand All @@ -45,18 +49,27 @@ impl BridgeableBoxedFnOnce {
pub fn convert_rust_value_to_ffi_compatible_value(
&self,
expression: &TokenStream,
types: &TypeDeclarations,
) -> TokenStream {
let args: Vec<TokenStream> = self.params.iter().map(|a| a.to_rust_type_path()).collect();
let ret = &self.ret.to_rust_type_path();
let args: Vec<TokenStream> = self
.params
.iter()
.map(|a| a.to_rust_type_path(types))
.collect();
let ret = &self.ret.to_rust_type_path(types);

quote! {
Box::into_raw(Box::new(#expression)) as *mut Box<dyn FnOnce(#(#args),*) -> #ret>
}
}

pub fn to_ffi_compatible_rust_type(&self) -> TokenStream {
let params: Vec<TokenStream> = self.params.iter().map(|a| a.to_rust_type_path()).collect();
let ret = &self.ret.to_rust_type_path();
pub fn to_ffi_compatible_rust_type(&self, types: &TypeDeclarations) -> TokenStream {
let params: Vec<TokenStream> = self
.params
.iter()
.map(|a| a.to_rust_type_path(types))
.collect();
let ret = &self.ret.to_rust_type_path(types);
quote! {
*mut Box<dyn FnOnce(#(#params),*) -> #ret>
}
Expand Down
Loading

0 comments on commit 832498a

Please sign in to comment.