diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift index 3844223b..50226d46 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Option.swift @@ -47,3 +47,22 @@ func swift_reflect_option_bool(arg: Optional) -> Optional { arg } + +func swift_reflect_option_string(arg: Optional) -> Optional { + arg +} +// TODO: Change `swift_arg_option_str` `swift_reflect_option_str` once we support Swift returning `-> &str` via RustStr +// For now we return true if the arg is Some and false if arg is None +//func swift_reflect_option_str(arg: Optional) -> Optional { +// arg +//} +func swift_arg_option_str(arg: Optional) -> Bool { + if let val = arg { + assert(val.toString() == "this is an option str") + return true + } else { + return false + } + +} + diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index d468ea8b..63eddc50 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -117,7 +117,12 @@ pub(crate) trait BridgeableType: Debug { /// Get the Swift representation of this type. /// /// For example, `Result` would become `RustResult` - fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String; + fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String; /// Get the C representation of this type. fn to_c_type(&self, types: &TypeDeclarations) -> String; @@ -146,6 +151,7 @@ pub(crate) trait BridgeableType: Debug { /// Get the FFI compatible Option representation. fn to_ffi_compatible_option_swift_type( &self, + type_pos: TypePosition, swift_bridge_path: &Path, types: &TypeDeclarations, ) -> String; @@ -207,6 +213,7 @@ pub(crate) trait BridgeableType: Debug { expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String; /// Convert an Option FFI representation to the Rust representation. @@ -285,6 +292,8 @@ pub(crate) trait BridgeableType: Debug { /// of checking the type. fn contains_ref_string_recursive(&self) -> bool; + // TODO: Is this used? Do we need this? + #[allow(unused)] /// Parse the type from a `FnArg`. fn from_fn_arg( fn_arg: &FnArg, @@ -532,8 +541,13 @@ impl BridgeableType for BridgedType { self.to_rust_type_path(types) } - fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { - self.to_swift_type(type_pos, types) + fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { + self.to_swift_type(type_pos, types, swift_bridge_path) } fn to_c_type(&self, types: &TypeDeclarations) -> String { @@ -562,6 +576,7 @@ impl BridgeableType for BridgedType { fn to_ffi_compatible_option_swift_type( &self, + _type_pos: TypePosition, _swift_bridge_path: &Path, _types: &TypeDeclarations, ) -> String { @@ -626,8 +641,9 @@ impl BridgeableType for BridgedType { expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { - self.convert_ffi_value_to_swift_value(expression, type_pos, types) + self.convert_ffi_value_to_swift_value(expression, type_pos, types, swift_bridge_path) } fn convert_ffi_option_expression_to_swift_type(&self, _expression: &str) -> String { @@ -1091,9 +1107,14 @@ impl BridgedType { // U8 -> UInt8 // *const u32 -> UnsafePointer // ... etc - pub fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + pub fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { match self { - BridgedType::Bridgeable(b) => b.to_swift_type(type_pos, types), + BridgedType::Bridgeable(b) => b.to_swift_type(type_pos, types, swift_bridge_path), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::U8 => "UInt8".to_string(), StdLibType::I8 => "Int8".to_string(), @@ -1119,7 +1140,7 @@ impl BridgedType { format!( "Unsafe{}Pointer<{}>", maybe_mutable, - ty.to_swift_type(type_pos, types) + ty.to_swift_type(type_pos, types, swift_bridge_path) ) } Pointee::Void(_) => { @@ -1136,7 +1157,7 @@ impl BridgedType { } else { format!( "UnsafeBufferPointer<{}>", - slice.ty.to_swift_type(type_pos, types) + slice.ty.to_swift_type(type_pos, types, swift_bridge_path) ) } } @@ -1167,26 +1188,37 @@ impl BridgedType { 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)) + format!( + "RustVec<{}>", + ty.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } 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)) + format!( + "RustVec<{}>", + ty.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } else { "UnsafeMutableRawPointer".to_string() } } _ => { - format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types)) + format!( + "RustVec<{}>", + ty.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } }, - StdLibType::Option(opt) => opt.to_swift_type(type_pos, types), - StdLibType::Result(result) => result.to_swift_type(type_pos, types), + StdLibType::Option(opt) => opt.to_swift_type(swift_bridge_path, type_pos, types), + StdLibType::Result(result) => { + result.to_swift_type(type_pos, types, swift_bridge_path) + } StdLibType::BoxedFnOnce(boxed_fn) => boxed_fn.to_swift_type().to_string(), - StdLibType::Tuple(tuple) => tuple.to_swift_type(type_pos, types), + StdLibType::Tuple(tuple) => tuple.to_swift_type(type_pos, types, swift_bridge_path), }, BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => { match type_pos { @@ -1482,11 +1514,15 @@ impl BridgedType { expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { match self { - BridgedType::Bridgeable(b) => { - b.convert_ffi_expression_to_swift_type(expression, type_pos, types) - } + BridgedType::Bridgeable(b) => b.convert_ffi_expression_to_swift_type( + expression, + type_pos, + types, + swift_bridge_path, + ), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null | StdLibType::U8 @@ -1530,7 +1566,7 @@ impl BridgedType { format!( "let slice = {value}; return UnsafeBufferPointer(start: slice.start.assumingMemoryBound(to: {ty}.self), count: Int(slice.len));", value = expression, - ty = ty.ty.to_swift_type(type_pos,types) + ty = ty.ty.to_swift_type(type_pos,types,swift_bridge_path) ) } StdLibType::Str => expression.to_string(), @@ -1538,15 +1574,21 @@ impl BridgedType { format!("RustVec(ptr: {})", expression) } StdLibType::Option(opt) => opt.convert_ffi_expression_to_swift_type(expression), - StdLibType::Result(result) => { - result.convert_ffi_value_to_swift_value(expression, type_pos, types) - } + StdLibType::Result(result) => result.convert_ffi_value_to_swift_value( + expression, + type_pos, + types, + swift_bridge_path, + ), StdLibType::BoxedFnOnce(fn_once) => { fn_once.convert_ffi_value_to_swift_value(type_pos) } - StdLibType::Tuple(tuple) => { - tuple.convert_ffi_expression_to_swift_type(expression, type_pos, types) - } + StdLibType::Tuple(tuple) => tuple.convert_ffi_expression_to_swift_type( + expression, + type_pos, + types, + swift_bridge_path, + ), }, BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => { shared_struct.convert_ffi_expression_to_swift_type(expression) 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 8b1fe2a0..ae74c104 100644 --- a/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs +++ b/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs @@ -99,12 +99,20 @@ impl BridgeableBoxedFnOnce { } /// arg0: UInt8, arg1: SomeType, ... - pub fn params_to_swift_types(&self, types: &TypeDeclarations) -> String { + pub fn params_to_swift_types( + &self, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { self.params .iter() .enumerate() .map(|(idx, ty)| { - let ty = ty.to_swift_type(TypePosition::FnArg(HostLang::Rust, idx), types); + let ty = ty.to_swift_type( + TypePosition::FnArg(HostLang::Rust, idx), + types, + swift_bridge_path, + ); format!("_ arg{idx}: {ty}") }) 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 953fd1ed..a8e17cbb 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs @@ -73,7 +73,12 @@ impl BridgeableType for BuiltInPointer { } } - fn to_swift_type(&self, _type_pos: TypePosition, _types: &TypeDeclarations) -> String { + fn to_swift_type( + &self, + _type_pos: TypePosition, + _types: &TypeDeclarations, + _swift_bridge_path: &Path, + ) -> String { todo!() } @@ -112,6 +117,7 @@ impl BridgeableType for BuiltInPointer { fn to_ffi_compatible_option_swift_type( &self, + _type_pos: TypePosition, _swift_bridge_path: &Path, _types: &TypeDeclarations, ) -> String { @@ -176,6 +182,7 @@ impl BridgeableType for BuiltInPointer { _expression: &str, _type_pos: TypePosition, _types: &TypeDeclarations, + _swift_bridge_path: &Path, ) -> String { todo!() } 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 46178323..f5e59e18 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs @@ -173,14 +173,22 @@ impl BuiltInResult { quote! { Result<#ok, #err> } } - pub fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + pub fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { match type_pos { - TypePosition::FnReturn(_) => self.ok_ty.to_swift_type(type_pos, types), + TypePosition::FnReturn(_) => { + self.ok_ty.to_swift_type(type_pos, types, swift_bridge_path) + } TypePosition::FnArg(_, _) | TypePosition::SharedStructField => { format!( "RustResult<{}, {}>", - self.ok_ty.to_swift_type(type_pos, types), - self.err_ty.to_swift_type(type_pos, types), + self.ok_ty.to_swift_type(type_pos, types, swift_bridge_path), + self.err_ty + .to_swift_type(type_pos, types, swift_bridge_path), ) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { @@ -207,6 +215,7 @@ impl BuiltInResult { expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { if self.is_custom_result_type() { if self.err_ty.can_be_encoded_with_zero_bytes() { @@ -222,12 +231,14 @@ impl BuiltInResult { "val.payload.ok", type_pos, types, + swift_bridge_path, ) }; let err_swift_type = self.err_ty.convert_ffi_expression_to_swift_type( "val.payload.err", type_pos, types, + swift_bridge_path, ); return match type_pos { @@ -266,18 +277,27 @@ impl BuiltInResult { } else { ok = " ".to_string() + &ok; } - let err = self - .err_ty - .convert_ffi_expression_to_swift_type("val!", type_pos, types); + let err = self.err_ty.convert_ffi_expression_to_swift_type( + "val!", + type_pos, + types, + swift_bridge_path, + ); return format!("try {{ let val = {expression}; if val != nil {{ throw {err} }} else {{ return{ok} }} }}()", expression = expression, err = err, ok = ok); } - let ok = self - .ok_ty - .convert_ffi_expression_to_swift_type("val.ok_or_err!", type_pos, types); - let err = - self.err_ty - .convert_ffi_expression_to_swift_type("val.ok_or_err!", type_pos, types); + let ok = self.ok_ty.convert_ffi_expression_to_swift_type( + "val.ok_or_err!", + type_pos, + types, + swift_bridge_path, + ); + let err = self.err_ty.convert_ffi_expression_to_swift_type( + "val.ok_or_err!", + type_pos, + types, + swift_bridge_path, + ); format!( "try {{ let val = {expression}; if val.is_ok {{ return {ok} }} else {{ throw {err} }} }}()", @@ -451,6 +471,7 @@ typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}} expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { if self.is_custom_result_type() { let ok = if self.ok_ty.can_be_encoded_with_zero_bytes() { @@ -460,12 +481,14 @@ typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}} &format!("{expression}.payload.ok"), type_pos, types, + swift_bridge_path, ) }; let err = self.err_ty.convert_ffi_expression_to_swift_type( &format!("{expression}.payload.err"), type_pos, types, + swift_bridge_path, ); return format!( r#"switch {expression}.tag {{ case {c_ok_tag_name}: wrapper.cb(.success({ok})) case {c_err_tag_name}: wrapper.cb(.failure({err})) default: fatalError() }}"#, @@ -476,8 +499,10 @@ typedef struct {c_enum_name}{{{c_tag_name} tag; union {c_fields_name} payload;}} c_err_tag_name = self.c_err_tag_name(types) ); } - let ok = self.ok_ty.to_swift_type(type_pos, types); - let err = self.err_ty.to_swift_type(type_pos, types); + let ok = self.ok_ty.to_swift_type(type_pos, types, swift_bridge_path); + let err = self + .err_ty + .to_swift_type(type_pos, types, swift_bridge_path); let (ok_val, err_val, condition) = if self.ok_ty.can_be_encoded_with_zero_bytes() { ( diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs index ee0745d1..f79e7628 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs @@ -1,2 +1,4 @@ +// TODO: impl BridgeableType for BridgedStr { } +#[allow(unused)] #[derive(Debug)] pub(crate) struct BridgeableStr; 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 72f32be3..f151115b 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs @@ -47,7 +47,12 @@ impl BridgeableType for BridgedString { quote! { String } } - fn to_swift_type(&self, type_pos: TypePosition, _types: &TypeDeclarations) -> String { + fn to_swift_type( + &self, + type_pos: TypePosition, + _types: &TypeDeclarations, + _swift_bridge_path: &Path, + ) -> String { match type_pos { TypePosition::FnArg(func_host_lang, _) => { if func_host_lang.is_rust() { @@ -96,10 +101,32 @@ impl BridgeableType for BridgedString { fn to_ffi_compatible_option_swift_type( &self, + type_pos: TypePosition, _swift_bridge_path: &Path, _types: &TypeDeclarations, ) -> String { - todo!() + match type_pos { + TypePosition::FnArg(host_lang, _) => { + if host_lang.is_rust() { + todo!() + } else { + "UnsafeMutableRawPointer?".to_string() + } + } + TypePosition::FnReturn(host_lang) => { + if host_lang.is_rust() { + todo!() + } else { + "UnsafeMutableRawPointer?".to_string() + } + } + TypePosition::SharedStructField => { + todo!() + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + todo!() + } + } } fn to_ffi_compatible_option_c_type(&self) -> String { @@ -158,8 +185,15 @@ impl BridgeableType for BridgedString { expression = expression ) } - TypePosition::FnReturn(_) => { - todo!("Need to come back and think through what should happen here...") + TypePosition::FnReturn(func_host_lang) => { + if func_host_lang.is_rust() { + todo!() + } else { + format!( + "{{ if let rustString = optionalStringIntoRustString({expression}) {{ rustString.isOwned = false; return rustString.ptr }} else {{ return nil }} }}()", + expression = expression + ) + } } TypePosition::SharedStructField => { todo!("Option fields in structs are not yet supported.") @@ -184,10 +218,14 @@ impl BridgeableType for BridgedString { fn convert_ffi_option_expression_to_rust_type(&self, expression: &TokenStream) -> TokenStream { quote! { - if #expression.is_null() { - None - } else { - Some(unsafe { Box::from_raw(#expression).0 } ) + { + let val = #expression; + + if val.is_null() { + None + } else { + Some( unsafe { Box::from_raw(val).0 } ) + } } } } @@ -197,6 +235,7 @@ impl BridgeableType for BridgedString { expression: &str, type_pos: TypePosition, _types: &TypeDeclarations, + _swift_bridge_path: &Path, ) -> String { match type_pos { TypePosition::FnArg(_, _) 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 be6c9bee..be01e057 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 @@ -69,7 +69,12 @@ impl BridgeableType for OpaqueForeignType { } } - fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { if self.host_lang.is_rust() { match type_pos { TypePosition::FnArg(func_host_lang, _) | TypePosition::FnReturn(func_host_lang) => { @@ -90,7 +95,10 @@ impl BridgeableType for OpaqueForeignType { "{}{}", class_name, self.generics - .angle_bracketed_generic_concrete_swift_types_string(types) + .angle_bracketed_generic_concrete_swift_types_string( + types, + swift_bridge_path + ) ) } else { format!("UnsafeMutableRawPointer") @@ -108,7 +116,10 @@ impl BridgeableType for OpaqueForeignType { "{}{}", class_name, self.generics - .angle_bracketed_generic_concrete_swift_types_string(types) + .angle_bracketed_generic_concrete_swift_types_string( + types, + swift_bridge_path + ) ) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { @@ -209,6 +220,7 @@ impl BridgeableType for OpaqueForeignType { fn to_ffi_compatible_option_swift_type( &self, + _type_pos: TypePosition, _swift_bridge_path: &Path, _types: &TypeDeclarations, ) -> String { @@ -482,6 +494,7 @@ impl BridgeableType for OpaqueForeignType { expression: &str, type_pos: TypePosition, _types: &TypeDeclarations, + _swift_bridge_path: &Path, ) -> String { let mut ty_name = self.ty.to_string(); 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 7020b31d..016b2c0d 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs @@ -168,7 +168,15 @@ impl BridgedOption { } StdLibType::Str => { quote! { - if #expression.start.is_null() { None } else { Some(#expression.to_str()) } + { + let val = #expression; + + if val.start.is_null() { + None + } else { + Some( val.to_str() ) + } + } } } StdLibType::Vec(_) => { @@ -295,9 +303,28 @@ impl BridgedOption { StdLibType::RefSlice(_) => { todo!("Option<&[T]> is not yet supported") } - StdLibType::Str => { - format!("{expression}AsRustStr", expression = expression) - } + StdLibType::Str => match type_pos { + TypePosition::FnArg(host_lang, _) => { + if host_lang.is_rust() { + format!("{expression}AsRustStr", expression = expression) + } else { + todo!() + } + } + TypePosition::FnReturn(host_lang) => { + if host_lang.is_rust() { + format!("{expression}AsRustStr", expression = expression) + } else { + todo!() + } + } + TypePosition::SharedStructField => { + todo!() + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + todo!() + } + }, StdLibType::Vec(_) => { format!( "{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()" @@ -334,24 +361,38 @@ impl BridgedOption { } } - pub fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + pub fn to_swift_type( + &self, + swift_bridge_path: &Path, + type_pos: TypePosition, + types: &TypeDeclarations, + ) -> String { match type_pos { TypePosition::FnArg(func_host_lang, _) => { if func_host_lang.is_swift() { - self.to_ffi_compatible_swift_type(&types) + self.to_ffi_compatible_swift_type(type_pos, swift_bridge_path, &types) } else { - format!("Optional<{}>", self.ty.to_swift_type(type_pos, types)) + format!( + "Optional<{}>", + self.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } } TypePosition::FnReturn(func_host_lang) => { if func_host_lang.is_swift() { - self.to_ffi_compatible_swift_type(&types) + self.to_ffi_compatible_swift_type(type_pos, swift_bridge_path, &types) } else { - format!("Optional<{}>", self.ty.to_swift_type(type_pos, types)) + format!( + "Optional<{}>", + self.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } } TypePosition::SharedStructField => { - format!("Optional<{}>", self.ty.to_swift_type(type_pos, types)) + format!( + "Optional<{}>", + self.ty.to_swift_type(type_pos, types, swift_bridge_path) + ) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() @@ -359,7 +400,12 @@ impl BridgedOption { } } - fn to_ffi_compatible_swift_type(&self, _types: &TypeDeclarations) -> String { + fn to_ffi_compatible_swift_type( + &self, + type_pos: TypePosition, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> String { match self.ty.deref() { BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => { @@ -387,9 +433,7 @@ impl BridgedOption { StdLibType::RefSlice(_) => { todo!() } - StdLibType::Str => { - todo!() - } + StdLibType::Str => "RustStr".to_string(), StdLibType::Vec(_) => { todo!() } @@ -409,8 +453,8 @@ impl BridgedOption { BridgedType::Foreign(_) => { todo!() } - BridgedType::Bridgeable(_) => { - todo!() + BridgedType::Bridgeable(bridgeable) => { + bridgeable.to_ffi_compatible_option_swift_type(type_pos, swift_bridge_path, types) } } } @@ -469,7 +513,6 @@ impl BridgedOption { #[cfg(test)] mod tests { use super::*; - use crate::TypeDeclarations; /// Verify that we can parse an `Option<&'static str>' bridged type /// This ensures that our logic that removes the spaces in order to normalize generic type 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 c9ad2ce2..301049a7 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 @@ -104,14 +104,20 @@ impl BridgeableType for BuiltInTuple { } } - fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + fn to_swift_type( + &self, + type_pos: TypePosition, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { match type_pos { TypePosition::FnArg(host_lang, _) => { if host_lang.is_swift() { let field_signatures = self.0.combine_field_types_into_ffi_name_string(types); format!("__swift_bridge__$tuple${field_signatures}") } else { - self.0.to_swift_tuple_signature(type_pos, types) + self.0 + .to_swift_tuple_signature(type_pos, types, swift_bridge_path) } } TypePosition::FnReturn(host_lang) => { @@ -119,7 +125,8 @@ impl BridgeableType for BuiltInTuple { let field_signatures = self.0.combine_field_types_into_ffi_name_string(types); format!("__swift_bridge__$tuple${field_signatures}") } else { - self.0.to_swift_tuple_signature(type_pos, types) + self.0 + .to_swift_tuple_signature(type_pos, types, swift_bridge_path) } } TypePosition::SharedStructField => todo!(), @@ -161,6 +168,7 @@ impl BridgeableType for BuiltInTuple { fn to_ffi_compatible_option_swift_type( &self, + _type_pos: TypePosition, _swift_bridge_path: &Path, _types: &TypeDeclarations, ) -> String { @@ -256,10 +264,14 @@ impl BridgeableType for BuiltInTuple { expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { - let converted_fields: Vec = self - .0 - .convert_ffi_expression_to_swift_type(expression, type_pos, types); + let converted_fields: Vec = self.0.convert_ffi_expression_to_swift_type( + expression, + type_pos, + types, + swift_bridge_path, + ); let converted_fields = converted_fields.join(", "); return format!( diff --git a/crates/swift-bridge-ir/src/bridged_type/shared_enum/enum_variant.rs b/crates/swift-bridge-ir/src/bridged_type/shared_enum/enum_variant.rs index fd8d3c0f..78bce00a 100644 --- a/crates/swift-bridge-ir/src/bridged_type/shared_enum/enum_variant.rs +++ b/crates/swift-bridge-ir/src/bridged_type/shared_enum/enum_variant.rs @@ -120,6 +120,7 @@ impl EnumVariant { &self, types: &TypeDeclarations, enum_name: String, + swift_bridge_path: &Path, ) -> String { let converted_fields: Vec = self .fields @@ -136,6 +137,7 @@ impl EnumVariant { ), TypePosition::SharedStructField, types, + swift_bridge_path, ); norm_field.struct_field_setter_string(field) }) diff --git a/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs b/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs index 4729bbfa..9ea8acc4 100644 --- a/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs +++ b/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs @@ -57,6 +57,7 @@ impl UnnamedStructFields { &self, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { let names: Vec = self .0 @@ -65,7 +66,7 @@ impl UnnamedStructFields { .map(|(_idx, field)| { BridgedType::new_with_type(&field.ty, types) .unwrap() - .to_swift_type(type_pos, types) + .to_swift_type(type_pos, types, swift_bridge_path) }) .collect(); let names = names.join(", "); @@ -127,14 +128,19 @@ impl UnnamedStructFields { _expression: &str, type_pos: TypePosition, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> Vec { self.0 .iter() .enumerate() .map(|(idx, field)| { let ty = BridgedType::new_with_type(&field.ty, types).unwrap(); - let converted_field = - ty.convert_ffi_value_to_swift_value(&format!("val._{idx}"), type_pos, types); + let converted_field = ty.convert_ffi_value_to_swift_value( + &format!("val._{idx}"), + type_pos, + types, + swift_bridge_path, + ); converted_field }) .collect() @@ -465,6 +471,7 @@ impl SharedStruct { &self, expression: &str, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { let name = self.swift_name_string(); let struct_name = &name; @@ -481,6 +488,7 @@ impl SharedStruct { &format!("val.{field_name}", field_name = field_name), TypePosition::SharedStructField, types, + swift_bridge_path, ); format!( diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs index e449ac62..fda0ccae 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/option_codegen_tests.rs @@ -204,6 +204,9 @@ void* __swift_bridge__$some_function(void* arg); } } +// TODO: Split this into two test modules, one for `Option` arg +// and another for `Option` return value. +// Easier to reason about when each codegen module is focused on one type in one position. /// Test code generation for Rust function that accepts and returns Option. mod extern_rust_fn_option_string { use super::*; @@ -225,10 +228,14 @@ mod extern_rust_fn_option_string { arg: *mut swift_bridge::string::RustString ) -> *mut swift_bridge::string::RustString { if let Some(val) = super::some_function( - if arg.is_null() { - None - } else { - Some(unsafe { Box::from_raw(arg).0 }) + { + let val = arg; + + if val.is_null() { + None + } else { + Some(unsafe { Box::from_raw(val).0 }) + } } ) { swift_bridge::string::RustString(val).box_into_raw() @@ -362,10 +369,14 @@ mod extern_rust_fn_arg_option_str { arg: swift_bridge::string::RustStr ) { super::some_function( - if arg.start.is_null() { - None - } else { - Some(arg.to_str()) + { + let val = arg; + + if val.start.is_null() { + None + } else { + Some(val.to_str()) + } } ) } @@ -391,7 +402,188 @@ void __swift_bridge__$some_function(struct RustStr arg); ); #[test] - fn extern_rust_fn_return_option_str() { + fn extern_rust_fn_arg_option_str() { + 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 Swift function that accepts an Option argument. +mod extern_swift_func_option_string_arg { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + fn some_function (arg: Option); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + pub fn some_function(arg: Option) { + unsafe { + __swift_bridge__some_function( + if let Some(val) = arg { + swift_bridge::string::RustString(val).box_into_raw() + } else { + std::ptr::null::() as *mut swift_bridge::string::RustString + } + ) + } + } + }, + quote! { + #[link_name = "__swift_bridge__$some_function"] + fn __swift_bridge__some_function(arg: *mut swift_bridge::string::RustString); + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$some_function") +func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer?) { + some_function(arg: { let val = arg; if val != nil { return RustString(ptr: val!) } else { return nil } }()) +} +"#, + ) + } + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(r#""#); + + #[test] + fn extern_swift_func_option_string_arg() { + 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 Swift function that returns an owned optional String argument. +mod extern_swift_func_returns_optional_string { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod foo { + extern "Swift" { + fn some_function () -> Option; + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + pub fn some_function () -> Option { + { + let val = unsafe { __swift_bridge__some_function() }; + + if val.is_null() { + None + } else { + Some( unsafe { Box::from_raw(val).0 }) + } + } + } + }, + quote! { + #[link_name = "__swift_bridge__$some_function"] + fn __swift_bridge__some_function() -> *mut swift_bridge::string::RustString ; + }, + ]) + } + + const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$some_function") +func __swift_bridge__some_function () -> UnsafeMutableRawPointer? { + { if let rustString = optionalStringIntoRustString(some_function()) { rustString.isOwned = false; return rustString.ptr } else { return nil } }() +} +"#, + ); + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(r#""#); + + #[test] + fn extern_swift_func_returns_optional_string() { + 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 Swift function that accepts an Option argument. +mod extern_swift_func_option_str_arg { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + fn some_function (arg: Option<&str>); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::ContainsMany(vec![ + quote! { + pub fn some_function(arg: Option<&str>) { + unsafe { + __swift_bridge__some_function( + if let Some(val) = arg { + swift_bridge::string::RustStr::from_str(val) + } else { + swift_bridge::string::RustStr { start: std::ptr::null::(), len: 0} + } + ) + } + } + }, + quote! { + #[link_name = "__swift_bridge__$some_function"] + fn __swift_bridge__some_function(arg: swift_bridge::string::RustStr); + }, + ]) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +@_cdecl("__swift_bridge__$some_function") +func __swift_bridge__some_function (_ arg: RustStr) { + some_function(arg: { let val = arg; if val.start != nil { return val; } else { return nil; } }()) +} +"#, + ) + } + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ExactAfterTrim(r#""#); + + #[test] + fn extern_swift_func_option_str_arg() { CodegenTest { bridge_module: bridge_module_tokens().into(), expected_rust_tokens: expected_rust_tokens(), diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs index 1578a313..baa7b24d 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/string_codegen_tests.rs @@ -263,15 +263,15 @@ func __swift_bridge__some_function () -> UnsafeMutableRawPointer { } } -/// Test code generation for Swift function that takes and returns an owned String argument. -mod extern_swift_func_takes_and_returns_string { +/// Test code generation for Swift function that takes an owned String argument. +mod extern_swift_func_takes_string_arg { use super::*; fn bridge_module_tokens() -> TokenStream { quote! { mod foo { extern "Swift" { - fn some_function (value: String) -> String; + fn some_function (value: String); } } } @@ -279,9 +279,11 @@ mod extern_swift_func_takes_and_returns_string { fn expected_rust_tokens() -> ExpectedRustTokens { ExpectedRustTokens::Contains(quote! { - pub fn some_function (value: String) -> String { + pub fn some_function (value: String) { unsafe { - Box :: from_raw (unsafe { __swift_bridge__some_function (swift_bridge :: string :: RustString (value) . box_into_raw ()) }) . 0 + __swift_bridge__some_function ( + swift_bridge :: string :: RustString (value) . box_into_raw () + ) } } }) @@ -290,8 +292,8 @@ mod extern_swift_func_takes_and_returns_string { const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ value: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { - { let rustString = some_function(value: RustString(ptr: value)).intoRustString(); rustString.isOwned = false; return rustString.ptr }() +func __swift_bridge__some_function (_ value: UnsafeMutableRawPointer) { + some_function(value: RustString(ptr: value)) } "#, ); 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 09eee96b..aba38a7a 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs @@ -532,7 +532,6 @@ mod tests { assert_trimmed_generated_contains_trimmed_expected, assert_trimmed_generated_equals_trimmed_expected, }; - use crate::SwiftBridgeModule; use super::*; diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift.rs b/crates/swift-bridge-ir/src/codegen/generate_swift.rs index e0af4a47..491cd818 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift.rs @@ -61,6 +61,7 @@ impl SwiftBridgeModule { .to_swift_type( TypePosition::FnReturn(opaque_ty.host_lang), &self.types, + &self.swift_bridge_path, ), }; class_protocols @@ -198,8 +199,8 @@ fn gen_function_exposes_swift_to_rust( func.sig.ident.to_string() }; - let params = func.to_swift_param_names_and_types(true, types); - let ret = func.to_swift_return_type(types); + let params = func.to_swift_param_names_and_types(true, types, swift_bridge_path); + let ret = func.to_swift_return_type(types, swift_bridge_path); let args = func.to_swift_call_args(false, true, types, swift_bridge_path); let mut call_fn = format!("{}({})", fn_name, args); @@ -253,15 +254,17 @@ fn gen_function_exposes_swift_to_rust( continue; } - let params_as_swift = boxed_fn.params_to_swift_types(types); + let params_as_swift = boxed_fn.params_to_swift_types(types, swift_bridge_path); let swift_ffi_call_args = boxed_fn.to_from_swift_to_rust_ffi_call_args(types); let maybe_ret = if boxed_fn.ret.is_null() { "".to_string() } else { - let ret = boxed_fn - .ret - .to_swift_type(TypePosition::FnArg(HostLang::Rust, idx), types); + let ret = boxed_fn.ret.to_swift_type( + TypePosition::FnArg(HostLang::Rust, idx), + types, + swift_bridge_path, + ); format!(" -> {}", ret) }; @@ -272,6 +275,7 @@ fn gen_function_exposes_swift_to_rust( &ret_value, TypePosition::FnReturn(HostLang::Rust), types, + swift_bridge_path, ); let maybe_generics = boxed_fn.maybe_swift_generics(types); 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 7207deb5..fe698efd 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 @@ -11,7 +11,7 @@ pub(super) fn gen_func_swift_calls_rust( swift_bridge_path: &Path, ) -> String { let fn_name = function.sig.ident.to_string(); - let params = function.to_swift_param_names_and_types(false, types); + let params = function.to_swift_param_names_and_types(false, types, swift_bridge_path); let call_args = function.to_swift_call_args(true, false, types, swift_bridge_path); let call_fn = if function.sig.asyncness.is_some() { let maybe_args = if function.sig.inputs.is_empty() { @@ -86,6 +86,7 @@ pub(super) fn gen_func_swift_calls_rust( &call_rust, TypePosition::FnReturn(function.host_lang), types, + swift_bridge_path, ) } else { if function.host_lang.is_swift() { @@ -185,22 +186,28 @@ pub(super) fn gen_func_swift_calls_rust( let maybe_return = if function.is_swift_initializer { "".to_string() } else { - function.to_swift_return_type(types) + function.to_swift_return_type(types, swift_bridge_path) }; let maybe_generics = function.maybe_swift_generics(types); let func_definition = if function.sig.asyncness.is_some() { let func_ret_ty = function.return_ty_built_in(types).unwrap(); - let rust_fn_ret_ty = - func_ret_ty.to_swift_type(TypePosition::FnReturn(HostLang::Rust), types); + let rust_fn_ret_ty = func_ret_ty.to_swift_type( + TypePosition::FnReturn(HostLang::Rust), + types, + swift_bridge_path, + ); let maybe_on_complete_sig_ret_val = if func_ret_ty.is_null() { "".to_string() } else { format!( ", rustFnRetVal: {}", - func_ret_ty - .to_swift_type(TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy, types) + func_ret_ty.to_swift_type( + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy, + types, + swift_bridge_path + ) ) }; let callback_wrapper_ty = format!("CbWrapper{}${}", maybe_type_name_segment, fn_name); @@ -210,6 +217,7 @@ pub(super) fn gen_func_swift_calls_rust( "rustFnRetVal", TypePosition::FnReturn(HostLang::Rust), types, + swift_bridge_path, ); ( run_wrapper_cb, @@ -225,6 +233,7 @@ pub(super) fn gen_func_swift_calls_rust( "rustFnRetVal", TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy, types, + swift_bridge_path, ) }; ( diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/opaque_copy_type.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/opaque_copy_type.rs index 9f4fe36a..7ae7fb64 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/opaque_copy_type.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/opaque_copy_type.rs @@ -32,7 +32,7 @@ pub(super) fn generate_opaque_copy_struct( if class_methods.owned_self_methods.len() > 0 {}; let struct_definition = if !ty.attributes.already_declared { - generate_struct_definition(ty, types) + generate_struct_definition(ty, types, swift_bridge_path) } else { "".to_string() }; @@ -47,6 +47,7 @@ pub(super) fn generate_opaque_copy_struct( fn generate_struct_definition( ty: &OpaqueForeignTypeDeclaration, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { let type_name = ty.ty.to_string(); let generics = ty.generics.angle_bracketed_generic_placeholders_string(); @@ -85,7 +86,9 @@ fn generate_struct_definition( ) } else { let ffi_repr_name = ty.ffi_repr_name_string(); - let bounds = ty.generics.rust_opaque_type_swift_generic_bounds(types); + let bounds = ty + .generics + .rust_opaque_type_swift_generic_bounds(types, swift_bridge_path); format!( r#"extension {type_name} @@ -105,7 +108,7 @@ extension {ffi_repr_name}: SwiftBridgeGenericCopyTypeFfiRepr {{}}"#, bounds = bounds, generics = ty .generics - .angle_bracketed_generic_concrete_swift_types_string(types), + .angle_bracketed_generic_concrete_swift_types_string(types, swift_bridge_path), ) }; 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 69a7647f..a1f03899 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 @@ -23,7 +23,11 @@ impl SwiftBridgeModule { for named_field in named_fields { let ty = BridgedType::new_with_type(&named_field.ty, &self.types) .unwrap() - .to_swift_type(TypePosition::SharedStructField, &self.types); + .to_swift_type( + TypePosition::SharedStructField, + &self.types, + &self.swift_bridge_path, + ); params.push(format!("{}: {}", named_field.name, ty)) } let params = params.join(", "); @@ -39,7 +43,11 @@ impl SwiftBridgeModule { for unnamed_field in unnamed_fields { let ty = BridgedType::new_with_type(&unnamed_field.ty, &self.types) .unwrap() - .to_swift_type(TypePosition::SharedStructField, &self.types); + .to_swift_type( + TypePosition::SharedStructField, + &self.types, + &self.swift_bridge_path, + ); params.push(ty); } let params = params.join(", "); @@ -78,8 +86,11 @@ impl SwiftBridgeModule { } for variant in shared_enum.variants.iter() { - let convert_ffi_variant_to_swift = - variant.convert_ffi_expression_to_swift(&self.types, format!("{}", enum_name)); + let convert_ffi_variant_to_swift = variant.convert_ffi_expression_to_swift( + &self.types, + format!("{}", enum_name), + &self.swift_bridge_path, + ); convert_ffi_repr_to_swift += &convert_ffi_variant_to_swift; } if convert_ffi_repr_to_swift.len() > 0 { diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs index 64864f0e..7453e7f5 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/shared_struct.rs @@ -44,8 +44,11 @@ impl SwiftBridgeModule { let convert_swift_to_ffi_repr = shared_struct.convert_swift_to_ffi_repr("self", &self.types); - let convert_ffi_repr_to_swift = - shared_struct.convert_ffi_expression_to_swift("self", &self.types); + let convert_ffi_repr_to_swift = shared_struct.convert_ffi_expression_to_swift( + "self", + &self.types, + &self.swift_bridge_path, + ); // No need to generate any code. Swift will automatically generate a // struct from our C header typedef that we generate for this struct. @@ -113,7 +116,11 @@ extension {option_ffi_name} {{ params += &format!( "{}: {},", field.swift_name_string(), - bridged_ty.to_swift_type(TypePosition::SharedStructField, &self.types) + bridged_ty.to_swift_type( + TypePosition::SharedStructField, + &self.types, + &self.swift_bridge_path + ) ); } @@ -160,7 +167,11 @@ extension {option_ffi_name} {{ fields += &format!( " public var {}: {}\n", field.swift_name_string(), - bridged_ty.to_swift_type(TypePosition::SharedStructField, &self.types) + bridged_ty.to_swift_type( + TypePosition::SharedStructField, + &self.types, + &self.swift_bridge_path + ) ); } diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs b/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs index 0658f5a1..3f083fbb 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift/swift_class.rs @@ -28,6 +28,7 @@ pub(super) fn generate_swift_class( &class_methods.ref_self_methods, &class_methods.ref_mut_self_methods, types, + swift_bridge_path, ) } @@ -39,6 +40,7 @@ fn create_class_declaration( ref_self_methods: &[String], ref_mut_self_methods: &[String], types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { let type_name = &ty.ty_name_ident().to_string(); let generics = ty.generics.angle_bracketed_generic_placeholders_string(); @@ -194,7 +196,9 @@ where {swift_generic_bounds} {{ }} }}"#, type_name = type_name, - swift_generic_bounds = ty.generics.rust_opaque_type_swift_generic_bounds(types), + swift_generic_bounds = ty + .generics + .rust_opaque_type_swift_generic_bounds(types, swift_bridge_path), free_func_name = ty.free_rust_opaque_type_ffi_name() ); } diff --git a/crates/swift-bridge-ir/src/parse/parse_struct.rs b/crates/swift-bridge-ir/src/parse/parse_struct.rs index b8d073ea..8b6dfea3 100644 --- a/crates/swift-bridge-ir/src/parse/parse_struct.rs +++ b/crates/swift-bridge-ir/src/parse/parse_struct.rs @@ -187,7 +187,7 @@ impl<'a> SharedStructDeclarationParser<'a> { mod tests { use super::*; use crate::test_utils::{parse_errors, parse_ok}; - use quote::{quote, ToTokens}; + use quote::quote; /// Verify that we can parse a struct with no fields. /// Structs with no fields always have an implicit `swift_repr = "struct"`. 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 88119fe4..b1798b90 100644 --- a/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs +++ b/crates/swift-bridge-ir/src/parse/type_declarations/generics.rs @@ -4,7 +4,7 @@ use crate::TypeDeclarations; use proc_macro2::TokenStream; use quote::quote; use std::ops::Deref; -use syn::TypeParam; +use syn::{Path, TypeParam}; pub(crate) const GENERIC_PLACEHOLDERS: [&'static str; 8] = ["A", "B", "C", "D", "E", "F", "G", "H"]; @@ -20,7 +20,11 @@ impl OpaqueRustTypeGenerics { /// For Rust type `SomeType`: /// A == UInt32, B == UInt64 - pub(crate) fn rust_opaque_type_swift_generic_bounds(&self, types: &TypeDeclarations) -> String { + pub(crate) fn rust_opaque_type_swift_generic_bounds( + &self, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { if self.generics.len() == 0 { return "".to_string(); } @@ -37,7 +41,11 @@ impl OpaqueRustTypeGenerics { .unwrap() // TODO: FnReturn isn't the real position.. Add a // new variant that makes more sense for our use case (generic bounds). - .to_swift_type(TypePosition::FnReturn(HostLang::Rust), types) + .to_swift_type( + TypePosition::FnReturn(HostLang::Rust), + types, + swift_bridge_path + ) ) }) .collect(); @@ -67,6 +75,7 @@ impl OpaqueRustTypeGenerics { pub(crate) fn angle_bracketed_generic_concrete_swift_types_string( &self, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { if self.generics.len() == 0 { return "".to_string(); @@ -82,7 +91,11 @@ impl OpaqueRustTypeGenerics { .unwrap() // TODO: FnReturn isn't the real position.. Add a // new variant that makes more sense for our use case (generic bounds). - .to_swift_type(TypePosition::FnReturn(HostLang::Rust), types) + .to_swift_type( + TypePosition::FnReturn(HostLang::Rust), + types, + swift_bridge_path + ) ) }) .collect(); diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn/to_swift_func.rs b/crates/swift-bridge-ir/src/parsed_extern_fn/to_swift_func.rs index 0863cd66..ebf4ffbb 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn/to_swift_func.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn/to_swift_func.rs @@ -10,6 +10,7 @@ impl ParsedExternFn { &self, include_receiver_if_present: bool, types: &TypeDeclarations, + swift_bridge_path: &Path, ) -> String { let mut params: Vec = vec![]; @@ -40,7 +41,11 @@ impl ParsedExternFn { } } - built_in.to_swift_type(TypePosition::FnArg(self.host_lang, arg_idx), types) + built_in.to_swift_type( + TypePosition::FnArg(self.host_lang, arg_idx), + types, + swift_bridge_path, + ) } else { todo!("Push to ParsedErrors") }; @@ -71,7 +76,7 @@ impl ParsedExternFn { include_receiver_if_present: bool, include_var_name: bool, types: &TypeDeclarations, - _swift_bridge_path: &Path, + swift_bridge_path: &Path, ) -> String { let mut args = vec![]; let inputs = &self.func.sig.inputs; @@ -120,6 +125,7 @@ impl ParsedExternFn { &arg, TypePosition::FnArg(self.host_lang, arg_idx), types, + swift_bridge_path, ) } } @@ -139,7 +145,11 @@ impl ParsedExternFn { args.join(", ") } - pub fn to_swift_return_type(&self, types: &TypeDeclarations) -> String { + pub fn to_swift_return_type( + &self, + types: &TypeDeclarations, + swift_bridge_path: &Path, + ) -> String { match &self.func.sig.output { ReturnType::Default => "".to_string(), ReturnType::Type(_, ty) => { @@ -155,7 +165,11 @@ impl ParsedExternFn { format!( " {}-> {}", maybe_throws, - built_in.to_swift_type(TypePosition::FnReturn(self.host_lang,), types) + built_in.to_swift_type( + TypePosition::FnReturn(self.host_lang,), + types, + swift_bridge_path + ) ) } else { todo!("Push ParsedErrors") @@ -208,7 +222,7 @@ mod tests { for (idx, expected) in expected.into_iter().enumerate() { assert_eq!( - functions[idx].to_swift_return_type(&module.types), + functions[idx].to_swift_return_type(&module.types, &module.swift_bridge_path), format!(" -> {}", expected) ); } @@ -237,7 +251,11 @@ mod tests { for method in methods { assert_eq!( - method.to_swift_param_names_and_types(false, &module.types), + method.to_swift_param_names_and_types( + false, + &module.types, + &module.swift_bridge_path + ), "" ); } @@ -265,7 +283,11 @@ mod tests { for (idx, expected) in expected.into_iter().enumerate() { assert_eq!( - functions[idx].to_swift_param_names_and_types(false, &module.types), + functions[idx].to_swift_param_names_and_types( + false, + &module.types, + &module.swift_bridge_path + ), format!("_ other: {}", expected) ); } diff --git a/crates/swift-integration-tests/src/function_attributes/args_into.rs b/crates/swift-integration-tests/src/function_attributes/args_into.rs index 993c0247..162e786e 100644 --- a/crates/swift-integration-tests/src/function_attributes/args_into.rs +++ b/crates/swift-integration-tests/src/function_attributes/args_into.rs @@ -26,6 +26,7 @@ fn test_args_into(_some_arg: TypeA, _another_arg: TypeB) {} struct TypeA; enum TypeB { + #[allow(unused)] Foo(u8), } diff --git a/crates/swift-integration-tests/src/opaque_type_attributes/already_declared.rs b/crates/swift-integration-tests/src/opaque_type_attributes/already_declared.rs index fa0a38bf..f72f832c 100644 --- a/crates/swift-integration-tests/src/opaque_type_attributes/already_declared.rs +++ b/crates/swift-integration-tests/src/opaque_type_attributes/already_declared.rs @@ -47,8 +47,10 @@ mod ffi2 { pub struct AlreadyDeclaredTypeTest; +#[allow(dead_code)] #[derive(Copy, Clone)] pub struct AlreadyDeclaredCopyTypeTest([u16; 2]); + impl AlreadyDeclaredTypeTest { fn new() -> Self { AlreadyDeclaredTypeTest diff --git a/crates/swift-integration-tests/src/option.rs b/crates/swift-integration-tests/src/option.rs index 6b0a2499..3ae42603 100644 --- a/crates/swift-integration-tests/src/option.rs +++ b/crates/swift-integration-tests/src/option.rs @@ -135,6 +135,11 @@ mod ffi { fn swift_reflect_option_f32(arg: Option) -> Option; fn swift_reflect_option_f64(arg: Option) -> Option; fn swift_reflect_option_bool(arg: Option) -> Option; + + fn swift_reflect_option_string(arg: Option) -> Option; + // TODO: Change to `swift_reflect_option_str` once we support Swift returning `-> &str` + fn swift_arg_option_str(arg: Option<&str>) -> bool; + // fn swift_reflect_option_str(arg: Option<&str>) -> Option<&str>; } } @@ -172,6 +177,21 @@ fn test_rust_calls_swift_option_primitive() { assert_eq!(ffi::swift_reflect_option_bool(Some(true)), Some(true)); assert_eq!(ffi::swift_reflect_option_bool(Some(false)), Some(false)); assert_eq!(ffi::swift_reflect_option_bool(None), None); + + assert_eq!(ffi::swift_reflect_option_string(None), None); + assert_eq!( + ffi::swift_reflect_option_string(Some("hello".to_string())), + Some("hello".to_string()) + ); + + // TODO: Change to `swift_reflect_option_str` once we support Swift returning `-> &str` + assert_eq!(ffi::swift_arg_option_str(None), false); + assert_eq!( + ffi::swift_arg_option_str(Some("this is an option str")), + true + ); + // assert_eq!(ffi::swift_reflect_option_str(None), None); + // assert_eq!(ffi::swift_reflect_option_str(Some("a str")), Some("a str")); } pub struct OptTestOpaqueRustType {