From 91f78986256521eb0fd320f7ae6449c9d18c2b52 Mon Sep 17 00:00:00 2001 From: NiwakaDev Date: Sat, 10 Aug 2024 16:30:52 +0900 Subject: [PATCH] feat: support throwing initializers --- .../ResultTests.swift | 23 ++++++ crates/swift-bridge-ir/src/bridged_type.rs | 9 +++ .../src/bridged_type/bridgeable_result.rs | 14 ++++ .../src/bridged_type/bridgeable_string.rs | 4 + .../src/bridged_type/bridged_opaque_type.rs | 4 + .../src/bridged_type/bridged_option.rs | 2 + .../src/bridged_type/built_in_tuple.rs | 2 + .../opaque_rust_type_codegen_tests.rs | 79 +++++++++++++++++++ .../generate_function_swift_calls_rust.rs | 34 ++++++-- .../src/parse/parse_extern_mod.rs | 52 ++++++++---- .../parse_extern_mod/function_attributes.rs | 6 +- .../swift-bridge-ir/src/parsed_extern_fn.rs | 10 ++- crates/swift-integration-tests/src/result.rs | 24 ++++++ examples/async-functions/main.swift | 2 +- 14 files changed, 241 insertions(+), 24 deletions(-) diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift index 17728902..058ec2e1 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift @@ -249,4 +249,27 @@ class ResultTests: XCTestCase { XCTAssertEqual(UInt32(i), value.val()) } } + + /// Verify that we can use throwing initializers defined on the Rust side. + func testThrowingInitializers() throws { + XCTContext.runActivity(named: "Should fail") { + _ in + do { + let throwingInitializer = try ThrowingInitializer(false) + } catch let error as ResultTransparentEnum { + if case .NamedField(data: -123) = error { + // This case should pass. + } else { + XCTFail() + } + } catch { + XCTFail() + } + } + XCTContext.runActivity(named: "Should succeed") { + _ in + let throwingInitializer = try! ThrowingInitializer(true) + XCTAssertEqual(throwingInitializer.val(), 123) + } + } } diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index 6b423f85..39f23e3c 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -411,6 +411,7 @@ pub(crate) enum TypePosition { FnReturn(HostLang), SharedStructField, SwiftCallsRustAsyncOnCompleteReturnTy, + ThrowingInit(HostLang), } /// &[T] @@ -1177,6 +1178,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } StdLibType::Null => "()".to_string(), @@ -1193,6 +1195,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), }, StdLibType::Vec(ty) => match type_pos { TypePosition::FnArg(func_host_lang, _) => { @@ -1215,6 +1218,7 @@ impl BridgedType { "UnsafeMutableRawPointer".to_string() } } + TypePosition::ThrowingInit(_) => unimplemented!(), _ => { format!( "RustVec<{}>", @@ -1243,6 +1247,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { shared_struct.ffi_name_string() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(shared_enum))) => { @@ -1259,6 +1264,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } } @@ -1567,6 +1573,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), }, PointerKind::Mut => expression.to_string(), }, @@ -1660,6 +1667,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), }, }, StdLibType::Str => match type_pos { @@ -1677,6 +1685,7 @@ impl BridgedType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), }, StdLibType::Vec(_) => { format!( 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 8caa85e2..55d794de 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs @@ -1,4 +1,5 @@ use crate::bridged_type::{BridgeableType, BridgedType, CFfiStruct, TypePosition}; +use crate::parse::HostLang; use crate::{TypeDeclarations, SWIFT_BRIDGE_PREFIX}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; @@ -207,6 +208,7 @@ impl BuiltInResult { } "__private__ResultPtrAndPtr".to_string() } + TypePosition::ThrowingInit(_) => todo!(), } } @@ -253,6 +255,18 @@ impl BuiltInResult { ), TypePosition::SharedStructField => todo!(), TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => todo!(), + TypePosition::ThrowingInit(lang) => { + match lang { + HostLang::Rust => format!( + "let val = {expression}; switch val.tag {{ case {c_ok_name}: self.init(ptr: val.payload.ok) case {c_err_name}: throw {err_swift_type} default: fatalError() }}", + expression = expression, + c_ok_name = c_ok_name, + c_err_name = c_err_name, + err_swift_type = err_swift_type + ), + HostLang::Swift => todo!(), + } + } }; } 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 b5bca782..29e9f59a 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs @@ -76,6 +76,7 @@ impl BridgeableType for BridgedString { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { "UnsafeMutableRawPointer?".to_string() } + TypePosition::ThrowingInit(_) => todo!(), } } @@ -130,6 +131,7 @@ impl BridgeableType for BridgedString { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { todo!() } + TypePosition::ThrowingInit(_) => todo!(), } } @@ -205,6 +207,7 @@ impl BridgeableType for BridgedString { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => todo!(), } } @@ -250,6 +253,7 @@ impl BridgeableType for BridgedString { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { format!("RustString(ptr: {}!)", expression) } + TypePosition::ThrowingInit(_) => todo!(), } } 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 053fe6fe..15049587 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 @@ -129,6 +129,7 @@ impl BridgeableType for OpaqueForeignType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } else { match type_pos { @@ -146,6 +147,7 @@ impl BridgeableType for OpaqueForeignType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } } @@ -396,6 +398,7 @@ impl BridgeableType for OpaqueForeignType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } } else { @@ -420,6 +423,7 @@ impl BridgeableType for OpaqueForeignType { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } } 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 016b2c0d..8bfa38cb 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs @@ -324,6 +324,7 @@ impl BridgedOption { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { todo!() } + TypePosition::ThrowingInit(_) => todo!(), }, StdLibType::Vec(_) => { format!( @@ -397,6 +398,7 @@ impl BridgedOption { TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } + TypePosition::ThrowingInit(_) => unimplemented!(), } } diff --git a/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs b/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs index bc28dbff..6911d8a3 100644 --- a/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs +++ b/crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs @@ -135,6 +135,7 @@ impl BridgeableType for BuiltInTuple { } TypePosition::SharedStructField => todo!(), TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => todo!(), + TypePosition::ThrowingInit(_) => todo!(), } } @@ -227,6 +228,7 @@ impl BridgeableType for BuiltInTuple { } TypePosition::SharedStructField => todo!(), TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => todo!(), + TypePosition::ThrowingInit(_) => todo!(), } } diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs index 2d2a6f03..40074f04 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_rust_type_codegen_tests.rs @@ -527,3 +527,82 @@ void* __swift_bridge__$Foo$new(void); .test(); } } + +/// Verify that we can use a Swift class with a throwing init. +mod extern_rust_class_with_throwing_init { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod foo { + enum SomeErrEnum { + Variant1, + Variant2, + } + extern "Rust" { + type Foo; + + #[swift_bridge(init)] + fn new() -> Result; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + # [export_name = "__swift_bridge__$Foo$new"] + pub extern "C" fn __swift_bridge__Foo_new() -> ResultFooAndSomeErrEnum{ + match super :: Foo :: new() { + Ok(ok) => ResultFooAndSomeErrEnum::Ok(Box::into_raw(Box::new({ + let val: super::Foo = ok; + val + })) as *mut super::Foo), + Err(err) => ResultFooAndSomeErrEnum::Err(err.into_ffi_repr()), + } + } + }) + } + + const EXPECTED_SWIFT: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( + r#" +public class Foo: FooRefMut { + var isOwned: Bool = true + + public override init(ptr: UnsafeMutableRawPointer) { + super.init(ptr: ptr) + } + + deinit { + if isOwned { + __swift_bridge__$Foo$_free(ptr) + } + } +} +extension Foo { + public convenience init() throws { + let val = __swift_bridge__$Foo$new(); switch val.tag { case __swift_bridge__$ResultFooAndSomeErrEnum$ResultOk: self.init(ptr: val.payload.ok) case __swift_bridge__$ResultFooAndSomeErrEnum$ResultErr: throw val.payload.err.intoSwiftRepr() default: fatalError() } + } +} +"#, + ); + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ContainsAfterTrim( + r#" +typedef enum __swift_bridge__$ResultFooAndSomeErrEnum$Tag {__swift_bridge__$ResultFooAndSomeErrEnum$ResultOk, __swift_bridge__$ResultFooAndSomeErrEnum$ResultErr} __swift_bridge__$ResultFooAndSomeErrEnum$Tag; +union __swift_bridge__$ResultFooAndSomeErrEnum$Fields {void* ok; struct __swift_bridge__$SomeErrEnum err;}; +typedef struct __swift_bridge__$ResultFooAndSomeErrEnum{__swift_bridge__$ResultFooAndSomeErrEnum$Tag tag; union __swift_bridge__$ResultFooAndSomeErrEnum$Fields payload;} __swift_bridge__$ResultFooAndSomeErrEnum; +"#, + ); + + #[test] + fn extern_rust_class_with_throwing_init() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: EXPECTED_SWIFT, + expected_c_header: EXPECTED_C_HEADER, + } + .test(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs index a52a9933..07bcad4f 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/generate_function_swift_calls_rust.rs @@ -1,5 +1,6 @@ use crate::bridged_type::{fn_arg_name, BridgeableType, BridgedType, StdLibType, TypePosition}; use crate::parse::{HostLang, TypeDeclaration}; +use crate::parsed_extern_fn::FailableInitializerType; use crate::{ParsedExternFn, TypeDeclarations, SWIFT_BRIDGE_PREFIX}; use quote::ToTokens; use std::ops::Deref; @@ -55,7 +56,13 @@ pub(super) fn gen_func_swift_calls_rust( if function.is_copy_method_on_opaque_type() { "public init".to_string() } else { - if function.is_swift_failable_initializer { + if let Some(crate::parsed_extern_fn::FailableInitializerType::Throwing) = + function.swift_failable_initializer + { + "public convenience init".to_string() + } else if let Some(crate::parsed_extern_fn::FailableInitializerType::Option) = + function.swift_failable_initializer + { "public convenience init?".to_string() } else { "public convenience init".to_string() @@ -69,6 +76,12 @@ pub(super) fn gen_func_swift_calls_rust( } }; + let maybe_throws = + if let Some(FailableInitializerType::Throwing) = function.swift_failable_initializer { + " throws" + } else { + "" + }; let indentation = if function.associated_type.is_some() { " " } else { @@ -84,7 +97,17 @@ pub(super) fn gen_func_swift_calls_rust( let mut call_rust = if function.sig.asyncness.is_some() { call_rust } else if function.is_swift_initializer { - call_rust + if let Some(FailableInitializerType::Throwing) = function.swift_failable_initializer { + let built_in = function.return_ty_built_in(types).unwrap(); + built_in.convert_ffi_value_to_swift_value( + &call_rust, + TypePosition::ThrowingInit(function.host_lang), + types, + swift_bridge_path, + ) + } else { + call_rust + } } else if let Some(built_in) = function.return_ty_built_in(types) { built_in.convert_ffi_value_to_swift_value( &call_rust, @@ -183,12 +206,12 @@ pub(super) fn gen_func_swift_calls_rust( if function.is_copy_method_on_opaque_type() { call_rust = format!("self.bytes = {}", call_rust) } else { - if function.is_swift_failable_initializer { + if let Some(FailableInitializerType::Option) = function.swift_failable_initializer { call_rust = format!( "guard let val = {} else {{ return nil }}; self.init(ptr: val)", call_rust ) - } else { + } else if function.swift_failable_initializer.is_none() { call_rust = format!("self.init(ptr: {})", call_rust) } } @@ -315,7 +338,7 @@ return{maybe_try}await {with_checked_continuation_function_name}({{ (continuatio ) } else { format!( - r#"{indentation}{maybe_static_class_func}{swift_class_func_name}{maybe_generics}({params}){maybe_ret} {{ + r#"{indentation}{maybe_static_class_func}{swift_class_func_name}{maybe_generics}({params}){maybe_throws}{maybe_ret} {{ {indentation} {call_rust} {indentation}}}"#, indentation = indentation, @@ -325,6 +348,7 @@ return{maybe_try}await {with_checked_continuation_function_name}({{ (continuatio params = params, maybe_ret = maybe_return, call_rust = call_rust, + maybe_throws = maybe_throws, ) }; diff --git a/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs b/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs index b15c1cfd..37a1a84e 100644 --- a/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs +++ b/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs @@ -10,7 +10,7 @@ use crate::parse::type_declarations::{ OpaqueForeignTypeDeclaration, TypeDeclaration, TypeDeclarations, }; use crate::parse::{HostLang, OpaqueRustTypeGenerics}; -use crate::parsed_extern_fn::fn_arg_is_mutable_reference; +use crate::parsed_extern_fn::{fn_arg_is_mutable_reference, FailableInitializerType}; use crate::ParsedExternFn; use proc_macro2::Ident; use quote::{format_ident, ToTokens}; @@ -111,13 +111,17 @@ impl<'a> ForeignModParser<'a> { } let return_type = &func.sig.output; - let mut is_swift_failable_initializer = false; + let mut swift_failable_initializer: Option = None; if let ReturnType::Type(_, return_ty) = return_type { let bridged_return_type = BridgedType::new_with_type(return_ty.deref(), &self.type_declarations); + if let Some(ty) = &bridged_return_type { if ty.as_option().is_some() && attributes.is_swift_initializer { - is_swift_failable_initializer = true; + swift_failable_initializer = Some(FailableInitializerType::Option); + } else if ty.as_result().is_some() && attributes.is_swift_initializer { + swift_failable_initializer = + Some(FailableInitializerType::Throwing); } } if bridged_return_type.is_none() { @@ -131,7 +135,7 @@ impl<'a> ForeignModParser<'a> { func.clone(), &attributes, &mut local_type_declarations, - is_swift_failable_initializer, + swift_failable_initializer.clone(), )?; if attributes.is_swift_identifiable { @@ -236,7 +240,7 @@ impl<'a> ForeignModParser<'a> { func, associated_type, is_swift_initializer: attributes.is_swift_initializer, - is_swift_failable_initializer: is_swift_failable_initializer, + swift_failable_initializer, is_swift_identifiable: attributes.is_swift_identifiable, host_lang, rust_name_override: attributes.rust_name, @@ -302,7 +306,7 @@ impl<'a> ForeignModParser<'a> { func: ForeignItemFn, attributes: &FunctionAttributes, local_type_declarations: &mut HashMap, - is_swift_failable_initializer: bool, + swift_failable_initializer: Option, ) -> syn::Result> { let associated_type = match first { Some(FnArg::Receiver(recv)) => { @@ -346,7 +350,7 @@ impl<'a> ForeignModParser<'a> { func.clone(), attributes, local_type_declarations, - is_swift_failable_initializer, + swift_failable_initializer, )?; associated_type } @@ -383,14 +387,32 @@ Otherwise we use a more general error that says that your argument is invalid. ty_string } }; - if is_swift_failable_initializer { - // Safety: since we've already checked ty_string is formatted as "Option<~>" before calling this function. - let last_bracket = ty_string.rfind(">").unwrap(); - - let inner = &ty_string[0..last_bracket]; - let inner = inner.trim_start_matches("Option < ").trim_end_matches(" "); - let ty = self.type_declarations.get(inner); - ty.map(|ty| ty.clone()) + if swift_failable_initializer.is_some() { + if ty_string.starts_with("Option <") { + // Safety: since we've already checked ty_string is formatted as "Option<~>" before calling this function. + let last_bracket = ty_string.rfind(">").unwrap(); + + let inner = &ty_string[0..last_bracket]; + let inner = inner.trim_start_matches("Option < ").trim_end_matches(" "); + + let ty = self.type_declarations.get(inner); + ty.map(|ty| ty.clone()) + } else if ty_string.starts_with("Result <") { + // A , B > + let trimmed = ty_string.trim_start_matches("Result < "); + // A , B + let trimmed = trimmed.trim_end_matches(" >"); + + // [A, B] + let ok_and_err = trimmed.rsplit_once(",").unwrap(); + let ok = ok_and_err.0.trim(); + + let ty = self.type_declarations.get(ok); + + ty.map(|ty| ty.clone()) + } else { + unreachable!(); + } } else { let ty = self.type_declarations.get(&ty_string); diff --git a/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs b/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs index 16b85b1f..3117a199 100644 --- a/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs +++ b/crates/swift-bridge-ir/src/parse/parse_extern_mod/function_attributes.rs @@ -165,6 +165,7 @@ impl Parse for FunctionAttr { #[cfg(test)] mod tests { use crate::errors::{FunctionAttributeParseError, IdentifiableParseError, ParseError}; + use crate::parsed_extern_fn::FailableInitializerType; use crate::test_utils::{parse_errors, parse_ok}; use quote::{quote, ToTokens}; @@ -295,7 +296,10 @@ mod tests { let func = &module.functions[0]; assert!(func.is_swift_initializer); - assert!(func.is_swift_failable_initializer); + matches!( + func.swift_failable_initializer, + Some(FailableInitializerType::Option) + ); } /// Verify that we can parse an init function that takes inputs. diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn.rs b/crates/swift-bridge-ir/src/parsed_extern_fn.rs index 2faa67b5..545413ab 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn.rs @@ -29,6 +29,13 @@ impl SwiftFuncGenerics { } } +/// Represents different types of Swift's initializers that can fail +#[derive(Clone)] +pub(crate) enum FailableInitializerType { + Throwing, + Option, +} + /// A method or associated function associated with a type. /// /// fn bar (&self); @@ -62,7 +69,7 @@ pub(crate) struct ParsedExternFn { /// Whether or not this function is a Swift failable initializer. /// For more details, see: /// [Swift Documentation - Failable Initializers](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Failable-Initializers) - pub is_swift_failable_initializer: bool, + pub swift_failable_initializer: Option, /// Whether or not this function should be used for the associated type's Swift /// `Identifiable` protocol implementation. pub is_swift_identifiable: bool, @@ -468,7 +475,6 @@ impl ParsedExternFn { } }) .unwrap_or("".to_string()); - format!( "{}{}${}", SWIFT_BRIDGE_PREFIX, diff --git a/crates/swift-integration-tests/src/result.rs b/crates/swift-integration-tests/src/result.rs index b1eda563..a905a048 100644 --- a/crates/swift-integration-tests/src/result.rs +++ b/crates/swift-integration-tests/src/result.rs @@ -89,6 +89,13 @@ mod ffi { succeed: bool, ) -> Result<(i32, ResultTestOpaqueRustType, String), ResultTransparentEnum>; } + + extern "Rust" { + type ThrowingInitializer; + #[swift_bridge(init)] + fn new(succeed: bool) -> Result; + fn val(&self) -> i32; + } } fn rust_func_takes_result_string(arg: Result) { @@ -247,3 +254,20 @@ fn rust_func_return_result_tuple_transparent_enum( Err(ffi::ResultTransparentEnum::NamedField { data: -123 }) } } + +struct ThrowingInitializer { + val: i32, +} + +impl ThrowingInitializer { + fn new(succeed: bool) -> Result { + if succeed { + Ok(ThrowingInitializer { val: 123 }) + } else { + Err(ffi::ResultTransparentEnum::NamedField { data: -123 }) + } + } + fn val(&self) -> i32 { + self.val + } +} diff --git a/examples/async-functions/main.swift b/examples/async-functions/main.swift index 468c4eb0..99d3db5d 100644 --- a/examples/async-functions/main.swift +++ b/examples/async-functions/main.swift @@ -14,4 +14,4 @@ Task { group.leave() } -group.wait() +group.wait() \ No newline at end of file