diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SharedEnumTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SharedEnumTests.swift index ff77b5d9..c4abcba0 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SharedEnumTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SharedEnumTests.swift @@ -33,27 +33,26 @@ 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() @@ -61,30 +60,72 @@ class SharedEnumTests: XCTestCase { } 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 + // 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 + // after we add support for methods on generic opaque Rust Types. + // See https://github.com/chinedufn/swift-bridge/issues/44#issuecomment-1114198605 + break + } } + } diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index bdaa68a2..ff675a19 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -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. /// @@ -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 { @@ -783,58 +766,43 @@ impl BridgedType { // U8 -> u8 // Vec -> Vec // 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! { @@ -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 @@ -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() { @@ -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(_) => { @@ -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; @@ -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), } } @@ -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))) => { diff --git a/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs b/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs index 689e7d89..1689b474 100644 --- a/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs +++ b/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs @@ -34,9 +34,13 @@ impl BridgeableBoxedFnOnce { } /// Box C> - pub fn to_rust_type_path(&self) -> TokenStream { - let args: Vec = 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 = self + .params + .iter() + .map(|a| a.to_rust_type_path(types)) + .collect(); + let ret = &self.ret.to_rust_type_path(types); quote! { Box #ret> } @@ -45,18 +49,27 @@ impl BridgeableBoxedFnOnce { pub fn convert_rust_value_to_ffi_compatible_value( &self, expression: &TokenStream, + types: &TypeDeclarations, ) -> TokenStream { - let args: Vec = self.params.iter().map(|a| a.to_rust_type_path()).collect(); - let ret = &self.ret.to_rust_type_path(); + let args: Vec = 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 #ret> } } - pub fn to_ffi_compatible_rust_type(&self) -> TokenStream { - let params: Vec = 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 = self + .params + .iter() + .map(|a| a.to_rust_type_path(types)) + .collect(); + let ret = &self.ret.to_rust_type_path(types); quote! { *mut Box #ret> } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs index 54b116d0..318ee204 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs @@ -1,5 +1,9 @@ -use crate::bridged_type::BridgedType; -use proc_macro2::TokenStream; +use crate::bridged_type::{ + BridgeableType, BridgedType, BuiltInResult, TypePosition, UnusedOptionNoneValue, +}; +use crate::parse::TypeDeclarations; +use crate::Path; +use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::{Debug, Formatter}; use syn::Type; @@ -24,31 +28,232 @@ pub(crate) enum Pointee { Void(Type), } -impl ToTokens for PointerKind { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl BridgeableType for BuiltInPointer { + fn is_built_in_type(&self) -> bool { + todo!() + } + + fn is_result(&self) -> bool { + todo!() + } + + fn as_result(&self) -> Option<&BuiltInResult> { + todo!() + } + + fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream { + match &self.pointee { + Pointee::BuiltIn(ty) => { + let pointer_kind = self.kind.to_ffi_compatible_rust_type(); + let ty = ty.to_rust_type_path(types); + quote! { #pointer_kind #ty} + } + Pointee::Void(_ty) => { + let pointer_kind = self.kind.to_ffi_compatible_rust_type(); + let pointee = self.pointee.to_rust_type_path(types); + + quote! { #pointer_kind super:: #pointee } + } + } + } + + fn to_swift_type(&self, _type_pos: TypePosition, _types: &TypeDeclarations) -> String { + todo!() + } + + fn to_c_type(&self) -> String { + todo!() + } + + fn to_c_include(&self) -> Option<&'static str> { + todo!() + } + + fn to_ffi_compatible_rust_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + let kind = self.kind.to_ffi_compatible_rust_type(); + + let ty = match &self.pointee { + Pointee::BuiltIn(ty) => ty.to_ffi_compatible_rust_type(swift_bridge_path, types), + Pointee::Void(ty) => { + quote! { super::#ty } + } + }; + + quote! { #kind #ty} + } + + fn to_ffi_compatible_option_rust_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn to_ffi_compatible_option_swift_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> String { + todo!() + } + + fn to_ffi_compatible_option_c_type(&self) -> String { + todo!() + } + + fn convert_rust_expression_to_ffi_type( + &self, + _expression: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + _span: Span, + ) -> TokenStream { + todo!() + } + + fn convert_option_rust_expression_to_ffi_type( + &self, + _expression: &TokenStream, + _swift_bridge_path: &Path, + ) -> TokenStream { + todo!() + } + + fn convert_swift_expression_to_ffi_type( + &self, + _expression: &str, + _type_pos: TypePosition, + ) -> String { + todo!() + } + + fn convert_option_swift_expression_to_ffi_type( + &self, + _expression: &str, + _type_pos: TypePosition, + ) -> String { + todo!() + } + + fn convert_ffi_expression_to_rust_type( + &self, + _expression: &TokenStream, + _span: Span, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn convert_ffi_option_expression_to_rust_type(&self, _expression: &TokenStream) -> TokenStream { + todo!() + } + + fn convert_ffi_expression_to_swift_type( + &self, + _expression: &str, + _type_pos: TypePosition, + _types: &TypeDeclarations, + ) -> String { + todo!() + } + + fn convert_ffi_option_expression_to_swift_type(&self, _expression: &str) -> String { + todo!() + } + + fn convert_ffi_result_ok_value_to_rust_value( + &self, + _ok_ffi_value: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn convert_ffi_result_err_value_to_rust_value( + &self, + _err_ffi_value: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn unused_option_none_val(&self, _swift_bridge_path: &Path) -> UnusedOptionNoneValue { + todo!() + } + + fn can_parse_token_stream_str(_tokens: &str) -> bool + where + Self: Sized, + { + todo!() + } + + fn from_type(_ty: &Type, _types: &TypeDeclarations) -> Option + where + Self: Sized, + { + todo!() + } + + fn parse_token_stream_str(_tokens: &str, _types: &TypeDeclarations) -> Option + where + Self: Sized, + { + todo!() + } + + fn is_null(&self) -> bool { + todo!() + } + + fn is_str(&self) -> bool { + todo!() + } + + fn contains_owned_string_recursive(&self) -> bool { + todo!() + } + + fn contains_ref_string_recursive(&self) -> bool { + todo!() + } + + fn has_swift_bridge_copy_annotation(&self) -> bool { + todo!() + } + + fn only_encoding(&self) -> Option { + todo!() + } +} + +impl PointerKind { + fn to_ffi_compatible_rust_type(&self) -> TokenStream { match self { PointerKind::Const => { - let t = quote! { *const }; - t.to_tokens(tokens); + quote! { *const } } PointerKind::Mut => { - let t = quote! { *mut }; - t.to_tokens(tokens); + quote! { *mut } } } } } -impl ToTokens for Pointee { - fn to_tokens(&self, tokens: &mut TokenStream) { +impl Pointee { + fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream { match self { - Pointee::BuiltIn(built_in) => { - built_in.to_rust_type_path().to_tokens(tokens); - } - Pointee::Void(ty) => { - ty.to_tokens(tokens); - } - }; + Pointee::BuiltIn(built_in) => built_in.to_rust_type_path(types), + Pointee::Void(ty) => ty.to_token_stream(), + } } } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs index 7b7c2459..43f5e8c5 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs @@ -125,9 +125,9 @@ impl BuiltInResult { } } - pub fn to_rust_type_path(&self) -> TokenStream { - let ok = self.ok_ty.to_rust_type_path(); - let err = self.err_ty.to_rust_type_path(); + pub fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream { + let ok = self.ok_ty.to_rust_type_path(types); + let err = self.err_ty.to_rust_type_path(types); quote! { Result<#ok, #err> } } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs index d5cab068..b888be1d 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs @@ -24,7 +24,7 @@ impl BridgeableType for BridgedString { None } - fn to_rust_type_path(&self) -> TokenStream { + fn to_rust_type_path(&self, _types: &TypeDeclarations) -> TokenStream { // FIXME: Change to `::std::string::String` quote! { String } } 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 132ec484..c1ea9f2a 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 @@ -34,12 +34,15 @@ impl BridgeableType for OpaqueForeignType { None } - fn to_rust_type_path(&self) -> TokenStream { + fn to_rust_type_path(&self, types: &TypeDeclarations) -> TokenStream { let ty_name = &self.ty; + let generics = self + .generics + .angle_bracketed_concrete_generics_tokens(types); if self.host_lang.is_rust() { quote! { - super:: #ty_name + super:: #ty_name #generics } } else { quote! { @@ -76,8 +79,19 @@ impl BridgeableType for OpaqueForeignType { } } TypePosition::SharedStructField => { - // - unimplemented!() + let class_name = self.ty.to_string(); + if !self.has_swift_bridge_copy_annotation { + if self.mutable || self.reference { + todo!(); + } + } + + format!( + "{}{}", + class_name, + self.generics + .angle_bracketed_generic_concrete_swift_types_string(types) + ) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() @@ -288,7 +302,10 @@ impl BridgeableType for OpaqueForeignType { } } TypePosition::SharedStructField => { - todo!("Opaque types in shared struct fields are not yet supported") + format!( + "{{{}.isOwned = false; return {}.ptr;}}()", + expression, expression + ) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs index a94224b5..5c0db645 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs @@ -255,7 +255,6 @@ extension __swift_bridge__$Option$SomeEnum { return nil } } - @inline(__always) static func fromSwiftRepr(_ val: Optional) -> __swift_bridge__$Option$SomeEnum { if let v = val { @@ -445,7 +444,6 @@ extension __swift_bridge__$SomeEnum { #include #include typedef struct __swift_bridge__$SomeEnum$FieldOfVariant1 {int32_t _0;} __swift_bridge__$SomeEnum$FieldOfVariant1; - union __swift_bridge__$SomeEnumFields { __swift_bridge__$SomeEnum$FieldOfVariant1 Variant1;}; typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$Variant1, __swift_bridge__$SomeEnum$Variant2, } __swift_bridge__$SomeEnumTag; typedef struct __swift_bridge__$SomeEnum { __swift_bridge__$SomeEnumTag tag; union __swift_bridge__$SomeEnumFields payload;} __swift_bridge__$SomeEnum; @@ -565,7 +563,6 @@ extension __swift_bridge__$SomeEnum { #include typedef struct __swift_bridge__$SomeEnum$FieldOfA {int32_t _0; uint32_t _1;} __swift_bridge__$SomeEnum$FieldOfA; typedef struct __swift_bridge__$SomeEnum$FieldOfB {void* _0;} __swift_bridge__$SomeEnum$FieldOfB; - union __swift_bridge__$SomeEnumFields { __swift_bridge__$SomeEnum$FieldOfA A; __swift_bridge__$SomeEnum$FieldOfB B;}; typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$A, __swift_bridge__$SomeEnum$B, } __swift_bridge__$SomeEnumTag; typedef struct __swift_bridge__$SomeEnum { __swift_bridge__$SomeEnumTag tag; union __swift_bridge__$SomeEnumFields payload;} __swift_bridge__$SomeEnum; @@ -700,7 +697,6 @@ extension __swift_bridge__$SomeEnum { #include typedef struct __swift_bridge__$SomeEnum$FieldOfA {int32_t data1; uint32_t data2;} __swift_bridge__$SomeEnum$FieldOfA; typedef struct __swift_bridge__$SomeEnum$FieldOfB {void* description;} __swift_bridge__$SomeEnum$FieldOfB; - union __swift_bridge__$SomeEnumFields { __swift_bridge__$SomeEnum$FieldOfA A; __swift_bridge__$SomeEnum$FieldOfB B;}; typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$A, __swift_bridge__$SomeEnum$B, } __swift_bridge__$SomeEnumTag; typedef struct __swift_bridge__$SomeEnum { __swift_bridge__$SomeEnumTag tag; union __swift_bridge__$SomeEnumFields payload;} __swift_bridge__$SomeEnum; @@ -720,3 +716,134 @@ typedef struct __swift_bridge__$Option$SomeEnum { bool is_some; __swift_bridge__ .test(); } } + +/// Verify that we generate an enum type that has each variant with a opaque type. +mod generates_enum_with_opaque_rust_data { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + #[swift_bridge::bridge] + mod ffi { + extern "Rust" { + type SomeType; + } + enum SomeEnum { + Unnamed(SomeType), + Named {data: SomeType} + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + #[derive ()] + pub enum SomeEnum { + Unnamed(super::SomeType), + Named {data: super::SomeType} + } + + #[repr(C)] + #[doc(hidden)] + pub enum __swift_bridge__SomeEnum { + Unnamed (*mut super::SomeType), + Named { + data: *mut super::SomeType + } + } + + impl swift_bridge::SharedEnum for SomeEnum { + type FfiRepr = __swift_bridge__SomeEnum; + } + + impl SomeEnum { + #[doc(hidden)] + #[inline(always)] + pub fn into_ffi_repr(self) -> __swift_bridge__SomeEnum { + match self { + SomeEnum::Unnamed(_0) => __swift_bridge__SomeEnum::Unnamed(Box::into_raw(Box::new({ + let val: super::SomeType = _0; + val + })) as *mut super::SomeType), + SomeEnum::Named{data} => __swift_bridge__SomeEnum::Named{data: Box::into_raw(Box::new({ + let val: super::SomeType = data; + val + })) as *mut super::SomeType} + } + } + } + + impl __swift_bridge__SomeEnum { + #[doc(hidden)] + #[inline(always)] + pub fn into_rust_repr(self) -> SomeEnum { + match self { + __swift_bridge__SomeEnum::Unnamed(_0) => SomeEnum::Unnamed(unsafe { * Box::from_raw(_0) }), + __swift_bridge__SomeEnum::Named{data} => SomeEnum::Named{data: unsafe { * Box::from_raw(data) }} + } + } + } + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +public enum SomeEnum { + case Unnamed(SomeType) + case Named(data: SomeType) +} +extension SomeEnum { + func intoFfiRepr() -> __swift_bridge__$SomeEnum { + switch self { + case SomeEnum.Unnamed(let _0): + return __swift_bridge__$SomeEnum(tag: __swift_bridge__$SomeEnum$Unnamed, payload: __swift_bridge__$SomeEnumFields(Unnamed: __swift_bridge__$SomeEnum$FieldOfUnnamed(_0: {_0.isOwned = false; return _0.ptr;}()))) + case SomeEnum.Named(let data): + return __swift_bridge__$SomeEnum(tag: __swift_bridge__$SomeEnum$Named, payload: __swift_bridge__$SomeEnumFields(Named: __swift_bridge__$SomeEnum$FieldOfNamed(data: {data.isOwned = false; return data.ptr;}()))) + } + } +} +extension __swift_bridge__$SomeEnum { + func intoSwiftRepr() -> SomeEnum { + switch self.tag { + case __swift_bridge__$SomeEnum$Unnamed: + return SomeEnum.Unnamed(SomeType(ptr: self.payload.Unnamed._0)) + case __swift_bridge__$SomeEnum$Named: + return SomeEnum.Named(data: SomeType(ptr: self.payload.Named.data)) + default: + fatalError("Unreachable") + } + } +} +"#, + ) + } + + fn expected_c_header() -> ExpectedCHeader { + ExpectedCHeader::ContainsManyAfterTrim(vec![ + r#" +#include +"#, + r#" +typedef struct __swift_bridge__$SomeEnum$FieldOfUnnamed {void* _0;} __swift_bridge__$SomeEnum$FieldOfUnnamed; +typedef struct __swift_bridge__$SomeEnum$FieldOfNamed {void* data;} __swift_bridge__$SomeEnum$FieldOfNamed; +union __swift_bridge__$SomeEnumFields { __swift_bridge__$SomeEnum$FieldOfUnnamed Unnamed; __swift_bridge__$SomeEnum$FieldOfNamed Named;}; +typedef enum __swift_bridge__$SomeEnumTag { __swift_bridge__$SomeEnum$Unnamed, __swift_bridge__$SomeEnum$Named, } __swift_bridge__$SomeEnumTag; +typedef struct __swift_bridge__$SomeEnum { __swift_bridge__$SomeEnumTag tag; union __swift_bridge__$SomeEnumFields payload;} __swift_bridge__$SomeEnum; +typedef struct __swift_bridge__$Option$SomeEnum { bool is_some; __swift_bridge__$SomeEnum val; } __swift_bridge__$Option$SomeEnum; +"#, + ]) + } + + #[test] + fn generates_enum_to_and_from_ffi_conversions_one_named_data_and_two_named_data() { + 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(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs index 47e9e1e5..160ad2f8 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs @@ -212,8 +212,7 @@ typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi } } let enum_decl = format!( - r#"{variant_fields} -union {ffi_union_name} {union_fields}; + r#"{variant_fields}union {ffi_union_name} {union_fields}; typedef enum {ffi_tag_name} {{ {variants}}} {ffi_tag_name}; typedef struct {ffi_name} {{ {ffi_tag_name} tag; union {ffi_union_name} payload;}} {ffi_name}; typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi_name};{maybe_vec_support}"#, diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs index 98d85c8e..3c3dfb87 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens.rs @@ -77,7 +77,9 @@ impl ToTokens for SwiftBridgeModule { } } TypeDeclaration::Shared(SharedTypeDeclaration::Enum(shared_enum)) => { - if let Some(definition) = self.generate_shared_enum_tokens(shared_enum) { + if let Some(definition) = + self.generate_shared_enum_tokens(shared_enum, &self.types) + { shared_enum_definitions.push(definition); } } diff --git a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_enum.rs b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_enum.rs index 90484115..f22a1ae4 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_enum.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_enum.rs @@ -3,9 +3,10 @@ use crate::bridged_type::{BridgedType, SharedEnum, StructFields}; use crate::codegen::generate_rust_tokens::vec::vec_of_transparent_enum::generate_vec_of_transparent_enum_functions; +use crate::parse::TypeDeclarations; use crate::{SwiftBridgeModule, SWIFT_BRIDGE_PREFIX}; use proc_macro2::TokenStream; -use quote::{format_ident, quote, ToTokens}; +use quote::{format_ident, quote}; use syn::Ident; impl SwiftBridgeModule { @@ -13,6 +14,7 @@ impl SwiftBridgeModule { pub(super) fn generate_shared_enum_tokens( &self, shared_enum: &SharedEnum, + types: &TypeDeclarations, ) -> Option { if shared_enum.already_declared { return None; @@ -36,7 +38,9 @@ impl SwiftBridgeModule { let mut names = vec![]; for named_field in named_fields { let field_name = &named_field.name; - let ty = named_field.ty.to_token_stream(); + let ty = BridgedType::new_with_type(&named_field.ty, &self.types) + .unwrap() + .to_rust_type_path(types); let field = quote! {#field_name : #ty}; names.push(field); } @@ -47,7 +51,9 @@ impl SwiftBridgeModule { StructFields::Unnamed(unamed_fields) => { let mut names = vec![]; for unnamed_field in unamed_fields { - names.push(unnamed_field.ty.to_token_stream()); + let ty = + BridgedType::new_with_type(&unnamed_field.ty, &self.types).unwrap(); + names.push(ty.to_rust_type_path(types)); } quote! { #variant_name (#(#names),*) diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_enum.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_enum.rs index c702af24..b726d281 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_enum.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_enum.rs @@ -151,7 +151,6 @@ extension {option_ffi_name} {{ return nil }} }} - @inline(__always) static func fromSwiftRepr(_ val: Optional<{enum_name}>) -> {option_ffi_name} {{ if let v = val {{ diff --git a/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs b/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs index 1a51b68b..88119fe4 100644 --- a/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs +++ b/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs @@ -105,7 +105,7 @@ impl OpaqueRustTypeGenerics { .iter() .map(|g| { let ty = BridgedType::new_with_str(&g.ident.to_string(), types).unwrap(); - let path = ty.to_rust_type_path(); + let path = ty.to_rust_type_path(types); quote! { #path } }) .collect(); diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs b/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs index a5560f7e..c81658c7 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs @@ -45,7 +45,7 @@ impl ParsedExternFn { } ReturnType::Type(arrow, _ty) => { if let Some(built_in) = BridgedType::new_with_return_type(&sig.output, types) { - let ty = built_in.maybe_convert_pointer_to_super_pointer(); + let ty = built_in.maybe_convert_pointer_to_super_pointer(types); let return_ty_span = sig.output.span(); quote_spanned! {return_ty_span=> #arrow #ty} @@ -113,7 +113,7 @@ impl ParsedExternFn { let boxed_fn_name = format!("{}{}_param{idx}", maybe_associated_ty, fn_name); let boxed_fn_name = Ident::new(&boxed_fn_name, fn_name.span()); - let boxed_fn_ffi_repr = boxed_fn.to_ffi_compatible_rust_type(); + let boxed_fn_ffi_repr = boxed_fn.to_ffi_compatible_rust_type(types); let free_boxed_fn_name = format!("free_{}{}_param{idx}", maybe_associated_ty, fn_name); let free_boxed_fn_name = Ident::new(&free_boxed_fn_name, fn_name.span()); @@ -204,7 +204,7 @@ impl ParsedExternFn { let pat = &pat_ty.pat; if let Some(built_in) = BridgedType::new_with_fn_arg(fn_arg, types) { - let ty = built_in.maybe_convert_pointer_to_super_pointer(); + let ty = built_in.maybe_convert_pointer_to_super_pointer(types); let maybe_unused = if built_in.can_be_encoded_with_zero_bytes() { "_" diff --git a/crates/swift-integration-tests/src/shared_types/shared_enum.rs b/crates/swift-integration-tests/src/shared_types/shared_enum.rs index 76658f54..552ce143 100644 --- a/crates/swift-integration-tests/src/shared_types/shared_enum.rs +++ b/crates/swift-integration-tests/src/shared_types/shared_enum.rs @@ -9,10 +9,18 @@ mod ffi { fn reflect_enum_with_no_data(arg: EnumWithNoData) -> EnumWithNoData; } + extern "Rust" { + #[swift_bridge(Equatable)] + type OpaqueRustForEnumTest; + + #[swift_bridge(init)] + fn new() -> OpaqueRustForEnumTest; + } + enum EnumWithUnnamedData { - Variant1(String, u32), - Variant2(i32, u8), - Variant3, + TwoFields(String, OpaqueRustForEnumTest), + OneField(i32), + NoFields, } extern "Rust" { @@ -20,14 +28,44 @@ mod ffi { } enum EnumWithNamedData { - Variant1 { hello: String, data_u8: u8 }, - Variant2 { data_i32: i32 }, - Variant3, + TwoFields { hello: String, data_u8: u8 }, + OneField { data_i32: i32 }, + NoFields, } extern "Rust" { fn reflect_enum_with_named_data(arg: EnumWithNamedData) -> EnumWithNamedData; } + + enum EnumWithOpaqueRust { + Named { data: OpaqueRustForEnumTest }, + Unnamed(OpaqueRustForEnumTest), + } + + extern "Rust" { + fn reflect_enum_with_opaque_type(arg: EnumWithOpaqueRust) -> EnumWithOpaqueRust; + } + + extern "Rust" { + #[swift_bridge(declare_generic)] + type GenericOpaqueRustForEnumTest; + + type GenericOpaqueRustForEnumTest; + fn new_generic_opaque_rust_for_enum_test() -> GenericOpaqueRustForEnumTest; + } + + enum EnumWithGenericOpaqueRust { + Named { + data: GenericOpaqueRustForEnumTest, + }, + Unnamed(GenericOpaqueRustForEnumTest), + } + + extern "Rust" { + fn reflect_enum_with_generic_opaque_type( + arg: EnumWithGenericOpaqueRust, + ) -> EnumWithGenericOpaqueRust; + } } fn reflect_enum_with_no_data(arg: ffi::EnumWithNoData) -> ffi::EnumWithNoData { @@ -41,3 +79,31 @@ fn reflect_enum_with_unnamed_data(arg: ffi::EnumWithUnnamedData) -> ffi::EnumWit fn reflect_enum_with_named_data(arg: ffi::EnumWithNamedData) -> ffi::EnumWithNamedData { arg } + +fn reflect_enum_with_opaque_type(arg: ffi::EnumWithOpaqueRust) -> ffi::EnumWithOpaqueRust { + arg +} + +#[derive(PartialEq)] +pub struct OpaqueRustForEnumTest; + +impl OpaqueRustForEnumTest { + fn new() -> Self { + OpaqueRustForEnumTest + } +} + +pub struct GenericOpaqueRustForEnumTest { + #[allow(unused)] + field: T, +} + +fn new_generic_opaque_rust_for_enum_test() -> GenericOpaqueRustForEnumTest { + GenericOpaqueRustForEnumTest { field: 123 } +} + +fn reflect_enum_with_generic_opaque_type( + arg: ffi::EnumWithGenericOpaqueRust, +) -> ffi::EnumWithGenericOpaqueRust { + arg +}