Skip to content

Commit

Permalink
Swift Option<String> and Option<&str> (#264)
Browse files Browse the repository at this point in the history
This commit adds support for passing `Option<String>` to and from
`extern "Swift"` functions, as well as for passing `Option<&str>` to
extern "Swift" functions.

For example, the following is now possible:

```rust
#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        fn opt_string_function(arg: Option<String>) -> Option<String>;

        fn opt_str_function(arg: Option<&str>);
    }
}
```

Note that you can not yet return `-> Option<&str>` from Swift.

This is an uncommon use case, so we're waiting until someone actually
needs it.
  • Loading branch information
chinedufn authored Mar 26, 2024
1 parent 53b118d commit 58f4a40
Show file tree
Hide file tree
Showing 27 changed files with 645 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,22 @@ func swift_reflect_option_bool(arg: Optional<Bool>) -> Optional<Bool> {
arg
}


func swift_reflect_option_string(arg: Optional<RustString>) -> Optional<RustString> {
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<RustStr>) -> Optional<RustStr> {
// arg
//}
func swift_arg_option_str(arg: Optional<RustStr>) -> Bool {
if let val = arg {
assert(val.toString() == "this is an option str")
return true
} else {
return false
}

}

90 changes: 66 additions & 24 deletions crates/swift-bridge-ir/src/bridged_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ pub(crate) trait BridgeableType: Debug {
/// Get the Swift representation of this type.
///
/// For example, `Result<String, ()>` would become `RustResult<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;

/// Get the C representation of this type.
fn to_c_type(&self, types: &TypeDeclarations) -> String;
Expand Down Expand Up @@ -146,6 +151,7 @@ pub(crate) trait BridgeableType: Debug {
/// Get the FFI compatible Option<Self> representation.
fn to_ffi_compatible_option_swift_type(
&self,
type_pos: TypePosition,
swift_bridge_path: &Path,
types: &TypeDeclarations,
) -> String;
Expand Down Expand Up @@ -207,6 +213,7 @@ pub(crate) trait BridgeableType: Debug {
expression: &str,
type_pos: TypePosition,
types: &TypeDeclarations,
swift_bridge_path: &Path,
) -> String;

/// Convert an Option<Self> FFI representation to the Rust representation.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1091,9 +1107,14 @@ impl BridgedType {
// U8 -> UInt8
// *const u32 -> UnsafePointer<UInt32>
// ... 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(),
Expand All @@ -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(_) => {
Expand All @@ -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)
)
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1530,23 +1566,29 @@ 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(),
StdLibType::Vec(_ty) => {
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)
Expand Down
12 changes: 10 additions & 2 deletions crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -176,6 +182,7 @@ impl BridgeableType for BuiltInPointer {
_expression: &str,
_type_pos: TypePosition,
_types: &TypeDeclarations,
_swift_bridge_path: &Path,
) -> String {
todo!()
}
Expand Down
Loading

0 comments on commit 58f4a40

Please sign in to comment.