Skip to content

Commit

Permalink
Swift Option<String> and Option<&str>
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 committed Mar 26, 2024
1 parent 53b118d commit 021f042
Show file tree
Hide file tree
Showing 25 changed files with 640 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
}

}

88 changes: 64 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 @@ -532,8 +539,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 +574,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 +639,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 +1105,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 +1138,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 +1155,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 +1186,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 +1512,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 +1564,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
55 changes: 40 additions & 15 deletions crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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() {
Expand All @@ -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 {
Expand Down Expand Up @@ -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} }} }}()",
Expand Down Expand Up @@ -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() {
Expand All @@ -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() }}"#,
Expand All @@ -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() {
(
Expand Down
Loading

0 comments on commit 021f042

Please sign in to comment.