From 54b9c09337701dd7580010d1d5ef6527b855ee98 Mon Sep 17 00:00:00 2001 From: Chinedu Francis Nwafili Date: Mon, 3 Oct 2022 17:49:47 -0400 Subject: [PATCH] Support opaque Swift types in results (#115) This commit allows you to use opaque Swift types as the `Ok` or `Err` variant for a result. For example, the following is now possible: ```rust #[swift_bridge::bridge] mod ffi { extern "Swift" { type SomeSwiftType; fn some_function(arg: Result<(), SomeSwiftType>); } } ``` --- Along the way we introduced a `trait BridgeableType`. We're going to move the `enum BridgedType` with the `trait BridgeableType` over time. --- .../project.pbxproj | 16 +- .../Boolean.swift | 20 - .../Primitive.swift | 57 + .../Result.swift | 12 + .../BooleanTests.swift | 28 - .../PrimitiveTests.swift | 34 + .../ResultTests.swift | 11 +- .../swift-bridge-build/src/generate_core.rs | 1 - crates/swift-bridge-ir/src/bridged_type.rs | 1217 ++++++++--------- .../src/bridged_type/boxed_fn.rs | 24 +- .../src/bridged_type/bridgeable_pointer.rs | 80 ++ .../src/bridged_type/bridgeable_primitive.rs | 9 + ...bridged_result.rs => bridgeable_result.rs} | 33 +- .../src/bridged_type/bridgeable_str.rs | 2 + .../src/bridged_type/bridgeable_string.rs | 257 ++++ .../src/bridged_type/bridged_opaque_type.rs | 659 +++++++++ .../src/bridged_type/bridged_option.rs | 170 +-- .../src/bridged_type/shared_struct.rs | 11 +- ..._opaque_swift_type_return_codegen_tests.rs | 8 +- .../opaque_swift_type_codegen_tests.rs | 4 +- .../codegen_tests/result_codegen_tests.rs | 65 + .../src/codegen/generate_c_header.rs | 8 +- .../src/codegen/generate_swift.rs | 24 +- .../generate_function_swift_calls_rust.rs | 13 +- .../src/parse/parse_extern_mod.rs | 31 +- .../src/parse/type_declarations.rs | 25 +- .../swift-bridge-ir/src/parsed_extern_fn.rs | 43 +- .../src/parsed_extern_fn/to_extern_c_fn.rs | 9 +- .../to_rust_impl_call_swift.rs | 4 +- .../src/parsed_extern_fn/to_swift_func.rs | 2 +- crates/swift-integration-tests/src/bool.rs | 22 - crates/swift-integration-tests/src/lib.rs | 2 +- .../swift-integration-tests/src/primitive.rs | 91 ++ crates/swift-integration-tests/src/result.rs | 22 + 34 files changed, 2002 insertions(+), 1012 deletions(-) delete mode 100644 SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Boolean.swift create mode 100644 SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Primitive.swift delete mode 100644 SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/BooleanTests.swift create mode 100644 SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/PrimitiveTests.swift create mode 100644 crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs create mode 100644 crates/swift-bridge-ir/src/bridged_type/bridgeable_primitive.rs rename crates/swift-bridge-ir/src/bridged_type/{bridged_result.rs => bridgeable_result.rs} (82%) create mode 100644 crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs create mode 100644 crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs create mode 100644 crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs delete mode 100644 crates/swift-integration-tests/src/bool.rs create mode 100644 crates/swift-integration-tests/src/primitive.rs diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj index f68dc3df..8ad930e8 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ 22046383282B4E3F00A09119 /* FunctionAttributeGetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22046382282B4E3F00A09119 /* FunctionAttributeGetTests.swift */; }; 221E16B42786233600F94AC0 /* ConditionalCompilationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221E16B32786233600F94AC0 /* ConditionalCompilationTests.swift */; }; 221E16B62786F9FF00F94AC0 /* OpaqueTypeAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221E16B52786F9FF00F94AC0 /* OpaqueTypeAttributeTests.swift */; }; + 222A81E928EB5BB100D4A412 /* Primitive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222A81E828EB5BB100D4A412 /* Primitive.swift */; }; + 222A81EB28EB5DF800D4A412 /* PrimitiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222A81EA28EB5DF800D4A412 /* PrimitiveTests.swift */; }; 22553324281DB5FC008A3121 /* GenericTests.rs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22553323281DB5FC008A3121 /* GenericTests.rs.swift */; }; 225908FC28DA0E320080C737 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225908FB28DA0E320080C737 /* ResultTests.swift */; }; 225908FE28DA0F9F0080C737 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225908FD28DA0F9F0080C737 /* Result.swift */; }; @@ -35,8 +37,6 @@ 228FE61227428A8D00805D9E /* OpaqueSwiftStructTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228FE61127428A8D00805D9E /* OpaqueSwiftStructTests.swift */; }; 228FE64627480E1D00805D9E /* SwiftBridgeCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228FE64427480E1C00805D9E /* SwiftBridgeCore.swift */; }; 228FE64A274919C600805D9E /* swift-integration-tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228FE648274919C500805D9E /* swift-integration-tests.swift */; }; - 228FE64E2749C3D700805D9E /* Boolean.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228FE64D2749C3D700805D9E /* Boolean.swift */; }; - 228FE6502749C43100805D9E /* BooleanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228FE64F2749C43100805D9E /* BooleanTests.swift */; }; 22BC10F62799283100A0D046 /* SharedStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BC10F52799283100A0D046 /* SharedStruct.swift */; }; 22BC10F82799A3A000A0D046 /* SharedStructAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BC10F72799A3A000A0D046 /* SharedStructAttributes.swift */; }; 22BCAAB927A2607700686A21 /* FunctionAttributeIdentifiableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BCAAB827A2607700686A21 /* FunctionAttributeIdentifiableTests.swift */; }; @@ -73,6 +73,8 @@ 22046382282B4E3F00A09119 /* FunctionAttributeGetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionAttributeGetTests.swift; sourceTree = ""; }; 221E16B32786233600F94AC0 /* ConditionalCompilationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalCompilationTests.swift; sourceTree = ""; }; 221E16B52786F9FF00F94AC0 /* OpaqueTypeAttributeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpaqueTypeAttributeTests.swift; sourceTree = ""; }; + 222A81E828EB5BB100D4A412 /* Primitive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Primitive.swift; sourceTree = ""; }; + 222A81EA28EB5DF800D4A412 /* PrimitiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimitiveTests.swift; sourceTree = ""; }; 22553323281DB5FC008A3121 /* GenericTests.rs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericTests.rs.swift; sourceTree = ""; }; 225908FB28DA0E320080C737 /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = ""; }; 225908FD28DA0F9F0080C737 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; @@ -95,8 +97,6 @@ 228FE6472749127400805D9E /* SwiftBridgeCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftBridgeCore.h; sourceTree = ""; }; 228FE648274919C500805D9E /* swift-integration-tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "swift-integration-tests.swift"; path = "swift-integration-tests/swift-integration-tests.swift"; sourceTree = ""; }; 228FE649274919C600805D9E /* swift-integration-tests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "swift-integration-tests.h"; path = "swift-integration-tests/swift-integration-tests.h"; sourceTree = ""; }; - 228FE64D2749C3D700805D9E /* Boolean.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Boolean.swift; sourceTree = ""; }; - 228FE64F2749C43100805D9E /* BooleanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BooleanTests.swift; sourceTree = ""; }; 22BC10F52799283100A0D046 /* SharedStruct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedStruct.swift; sourceTree = ""; }; 22BC10F72799A3A000A0D046 /* SharedStructAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedStructAttributes.swift; sourceTree = ""; }; 22BCAAB827A2607700686A21 /* FunctionAttributeIdentifiableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionAttributeIdentifiableTests.swift; sourceTree = ""; }; @@ -156,10 +156,10 @@ children = ( 228FE5D82740DB6D00805D9E /* Assets.xcassets */, 228FE60B2740F42000805D9E /* ASwiftStack.swift */, - 228FE64D2749C3D700805D9E /* Boolean.swift */, 228FE5D62740DB6A00805D9E /* ContentView.swift */, 22043296274B0AB000BAE645 /* Option.swift */, 220432A8274D31DC00BAE645 /* Pointer.swift */, + 222A81E828EB5BB100D4A412 /* Primitive.swift */, 228FE5DA2740DB6D00805D9E /* Preview Content */, 220432E92753092C00BAE645 /* RustFnUsesOpaqueSwiftType.swift */, 22BC10F52799283100A0D046 /* SharedStruct.swift */, @@ -187,7 +187,6 @@ isa = PBXGroup; children = ( 22D092A227B7E865009A4C2B /* AsyncTests.swift */, - 228FE64F2749C43100805D9E /* BooleanTests.swift */, 221E16B32786233600F94AC0 /* ConditionalCompilationTests.swift */, 22046382282B4E3F00A09119 /* FunctionAttributeGetTests.swift */, 22BCAAB827A2607700686A21 /* FunctionAttributeIdentifiableTests.swift */, @@ -197,6 +196,7 @@ 22043294274ADA7A00BAE645 /* OptionTests.swift */, 225908FB28DA0E320080C737 /* ResultTests.swift */, 220432A6274C953E00BAE645 /* PointerTests.swift */, + 222A81EA28EB5DF800D4A412 /* PrimitiveTests.swift */, 220432EB27530AFC00BAE645 /* RustFnUsesOpaqueSwiftTypeTests.swift */, 2202BC0727B2DD1700D43CC4 /* SharedEnumTests.swift */, 22C0AD50278ECA9E00A96469 /* SharedStructAttributeTests.swift */, @@ -371,7 +371,7 @@ 220432A9274D31DC00BAE645 /* Pointer.swift in Sources */, 225908FE28DA0F9F0080C737 /* Result.swift in Sources */, 228FE5D72740DB6A00805D9E /* ContentView.swift in Sources */, - 228FE64E2749C3D700805D9E /* Boolean.swift in Sources */, + 222A81E928EB5BB100D4A412 /* Primitive.swift in Sources */, 228FE64627480E1D00805D9E /* SwiftBridgeCore.swift in Sources */, 228FE5D52740DB6A00805D9E /* SwiftRustIntegrationTestRunnerApp.swift in Sources */, 228FE64A274919C600805D9E /* swift-integration-tests.swift in Sources */, @@ -391,9 +391,9 @@ 220432A7274C953E00BAE645 /* PointerTests.swift in Sources */, 220432AF274E7BF800BAE645 /* SharedStructTests.swift in Sources */, 220432EC27530AFC00BAE645 /* RustFnUsesOpaqueSwiftTypeTests.swift in Sources */, + 222A81EB28EB5DF800D4A412 /* PrimitiveTests.swift in Sources */, 22553324281DB5FC008A3121 /* GenericTests.rs.swift in Sources */, 225908FC28DA0E320080C737 /* ResultTests.swift in Sources */, - 228FE6502749C43100805D9E /* BooleanTests.swift in Sources */, 2202BC0827B2DD1700D43CC4 /* SharedEnumTests.swift in Sources */, 22BCAAB927A2607700686A21 /* FunctionAttributeIdentifiableTests.swift in Sources */, 22EE4E0B28B538A700FEC83C /* SwiftFnUsesOpaqueSwiftTypeTests.swift in Sources */, diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Boolean.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Boolean.swift deleted file mode 100644 index b2aefaa8..00000000 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Boolean.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Boolean.swift -// SwiftRustIntegrationTestRunner -// -// Created by Frankie Nwafili on 11/20/21. -// - -import Foundation - -public func runBoolTest() { - run_bool_test() -} - -public func rustNegateBool(_ bool: Bool) -> Bool { - rust_negate_bool(bool) -} - -public func swiftNegateBool(start: Bool) -> Bool { - !start -} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Primitive.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Primitive.swift new file mode 100644 index 00000000..44edf341 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Primitive.swift @@ -0,0 +1,57 @@ +// +// Primitive.swift +// SwiftRustIntegrationTestRunner +// +// Created by Frankie Nwafili on 10/3/22. +// + +import Foundation + +func swift_double_u8(arg: UInt8) -> UInt8 { + arg * 2 +} + +func swift_double_i8(arg: Int8) -> Int8 { + arg * 2 +} + +func swift_double_u16(arg: UInt16) -> UInt16 { + arg * 2 +} + +func swift_double_i16(arg: Int16) -> Int16 { + arg * 2 +} + +func swift_double_u32(arg: UInt32) -> UInt32 { + arg * 2 +} + +func swift_double_i32(arg: Int32) -> Int32 { + arg * 2 + +} + +func swift_double_u64(arg: UInt64) -> UInt64 { + arg * 2 +} + +func swift_double_i64(arg: Int64) -> Int64 { + arg * 2 +} + +func swift_double_f32(arg: Float) -> Float { + arg * 2.0 +} + +func swift_double_f64(arg: Double) -> Double { + arg * 2.0 +} + +func swift_negate_bool(arg: Bool) -> Bool { + !arg +} + +func swift_reflect_null(arg: ()) -> () { + arg +} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Result.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Result.swift index dacecc0f..03b8015b 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Result.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunner/Result.swift @@ -10,3 +10,15 @@ func swift_func_takes_callback_with_result_arg( ) { arg(.Ok(CallbackTestOpaqueRustType(555))) } + +public class ResultTestOpaqueSwiftType { + var num: UInt32 + + init(val: UInt32) { + self.num = val + } + + func val() -> UInt32 { + self.num + } +} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/BooleanTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/BooleanTests.swift deleted file mode 100644 index 30de4969..00000000 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/BooleanTests.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// BooleanTests.swift -// SwiftRustIntegrationTestRunnerTests -// -// Created by Frankie Nwafili on 11/20/21. -// - -import XCTest -@testable import SwiftRustIntegrationTestRunner - -class BooleanTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testRustTests() throws { - runBoolTest() - } - - func testSwiftTests() throws { - XCTAssertEqual(rustNegateBool(true), false) - XCTAssertEqual(rustNegateBool(false), true) - } -} diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/PrimitiveTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/PrimitiveTests.swift new file mode 100644 index 00000000..0ad23689 --- /dev/null +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/PrimitiveTests.swift @@ -0,0 +1,34 @@ +// +// PrimitiveTests.swift +// SwiftRustIntegrationTestRunnerTests +// +// Created by Frankie Nwafili on 10/3/22. +// + +import XCTest +@testable import SwiftRustIntegrationTestRunner + +/// Tests for generic types such as `type SomeType` +class PrimitiveTests: XCTestCase { + /// Run tests where Rust calls Swift functions that take primitive args. + func testRustCallsSwiftPrimitives() throws { + test_rust_calls_swift_primitives() + } + + /// Run tests where Swift calls Rust functions that take primitive args. + func testSwiftCallsRustPrimitives() throws { + XCTAssertEqual(rust_double_u8(10), 20); + XCTAssertEqual(rust_double_i8(10), 20); + XCTAssertEqual(rust_double_u16(10), 20); + XCTAssertEqual(rust_double_i16(10), 20); + XCTAssertEqual(rust_double_u32(10), 20); + XCTAssertEqual(rust_double_i32(10), 20); + XCTAssertEqual(rust_double_u64(10), 20); + XCTAssertEqual(rust_double_i64(10), 20); + XCTAssertEqual(rust_double_f32(10.0), 20.0); + XCTAssertEqual(rust_double_f64(10.0), 20.0); + XCTAssertEqual(rust_negate_bool(true), false); + XCTAssertEqual(rust_negate_bool(false), true); + } +} + diff --git a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift index 74fc4909..4897dc02 100644 --- a/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift +++ b/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/ResultTests.swift @@ -3,7 +3,6 @@ // SwiftRustIntegrationTestRunnerTests // // Created by Frankie Nwafili on 9/20/22. -// import XCTest @testable import SwiftRustIntegrationTestRunner @@ -24,4 +23,14 @@ class ResultTests: XCTestCase { .Err(ResultTestOpaqueRustType(222)) ) } + + /// Verify that we can pass a Result from Swift -> Rust + func testSwiftCallRustResultOpaqueSwift() throws { + rust_func_takes_result_opaque_swift( + .Ok(ResultTestOpaqueSwiftType(val: 555)) + ) + rust_func_takes_result_opaque_swift( + .Err(ResultTestOpaqueSwiftType(val: 666)) + ) + } } diff --git a/crates/swift-bridge-build/src/generate_core.rs b/crates/swift-bridge-build/src/generate_core.rs index 7801288a..b433547e 100644 --- a/crates/swift-bridge-build/src/generate_core.rs +++ b/crates/swift-bridge-build/src/generate_core.rs @@ -82,7 +82,6 @@ fn core_c_header() -> String { #include typedef struct RustStr { uint8_t* const start; uintptr_t len; } RustStr; typedef struct __private__FfiSlice { void* const start; uintptr_t len; } __private__FfiSlice; -typedef struct __private__PointerToSwiftType { void* ptr; } __private__RustHandleToSwiftType; void* __swift_bridge__null_pointer(void); typedef struct __private__OptionU8 { uint8_t val; bool is_some; } __private__OptionU8; diff --git a/crates/swift-bridge-ir/src/bridged_type.rs b/crates/swift-bridge-ir/src/bridged_type.rs index 09cda2bc..fa8e375f 100644 --- a/crates/swift-bridge-ir/src/bridged_type.rs +++ b/crates/swift-bridge-ir/src/bridged_type.rs @@ -1,15 +1,18 @@ -use std::fmt::{Debug, Formatter}; +use std::fmt::Debug; use std::ops::Deref; use std::str::FromStr; -use crate::bridged_type::boxed_fn::BuiltInBoxedFnOnce; -use crate::bridged_type::bridged_result::BuiltInResult; use proc_macro2::{Ident, Span, TokenStream}; use quote::ToTokens; use quote::{quote, quote_spanned}; use syn::{FnArg, Pat, PatType, Path, ReturnType, Type}; -use crate::parse::{HostLang, OpaqueRustTypeGenerics, TypeDeclarations}; +pub(crate) use self::bridged_opaque_type::OpaqueForeignType; +use crate::bridged_type::boxed_fn::BridgeableBoxedFnOnce; +use crate::bridged_type::bridgeable_pointer::{BuiltInPointer, Pointee, PointerKind}; +use crate::bridged_type::bridgeable_result::BuiltInResult; +use crate::bridged_type::bridgeable_string::BridgedString; +use crate::parse::{HostLang, TypeDeclaration, TypeDeclarations}; use crate::SWIFT_BRIDGE_PREFIX; use self::bridged_option::BridgedOption; @@ -17,24 +20,276 @@ pub(crate) use self::shared_enum::{EnumVariant, SharedEnum}; pub(crate) use self::shared_struct::{SharedStruct, StructFields, StructSwiftRepr}; pub(crate) mod boxed_fn; +mod bridgeable_pointer; +mod bridgeable_primitive; +mod bridgeable_result; +pub mod bridgeable_str; +pub mod bridgeable_string; +pub mod bridged_opaque_type; mod bridged_option; -mod bridged_result; mod shared_enum; pub(crate) mod shared_struct; -#[derive(Debug, PartialEq, Clone)] +/// Represents a type that can be passed between Rust and Swift. +// TODO: Move away from `BridgedType` and instead use `Box`. +// Our patterns have more or less stabilized and every type ends up implementing the same methods +// just in different ways. +// So, instead of a big enum with lots of methods that each match on the `BridgedType`, we can +// implement the BridgeableType trait for each type that we support. +// For opaque types we would just `impl BridgeableType for OpaqueForeignType`. +// We're essentially just moving a bunch of code around such that code for the same type lives +// next to each other instead of being spread out all over a bunch of match statements. +// +// TODO: Debug bounds is only used for a couple of tests. Remove these bounds and refactor those +// tests. +pub(crate) trait BridgeableType: Debug { + /// Whether or not this is a built-in supported type such as `u8`. + fn is_built_in_type(&self) -> bool; + + /// Whether or not this is a custom type such as `type SomeRustType`. + fn is_custom_type(&self) -> bool { + !self.is_built_in_type() + } + + /// Get the Rust representation of this type. + /// For a string this might be `std::string::String`. + fn to_rust_type_path(&self) -> TokenStream; + + /// Get the Swift representation of this type. + /// + /// For example, `Result` would become `RustResult` + fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String; + + /// Get the C representation of this type. + fn to_c_type(&self) -> String; + + /// Generate a C include statement to put in the C header. + /// For example, for a `u8` we would generate a `#include ` line. + fn to_c_include(&self) -> Option<&'static str>; + + /// Get the FFI compatible Rust type. + /// + /// For `String` this would be `*mut std::string::String`. + /// For `u8` this would be `u8`. + fn to_ffi_compatible_rust_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// Get the FFI compatible Option representation. + fn to_ffi_compatible_option_rust_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// Get the FFI compatible Option representation. + fn to_ffi_compatible_option_swift_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> String; + + /// Get the FFI compatible Option representation. + fn to_ffi_compatible_option_c_type(&self) -> String; + + /// Convert a Rust expression to an FFI compatible type. + fn convert_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// Convert a an `Option` Rust expression to an FFI compatible type. + fn convert_option_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + swift_bridge_path: &Path, + ) -> TokenStream; + + /// Convert a Swift expression into an FFI compatible type. + fn convert_swift_expression_to_ffi_type( + &self, + expression: &str, + type_pos: TypePosition, + ) -> String; + + /// Convert a an `Option` Rust expression to an FFI compatible type. + fn convert_option_swift_expression_to_ffi_type( + &self, + expression: &str, + type_pos: TypePosition, + ) -> String; + + /// Convert an FFI expression to this type's Rust representation. + /// + /// # Examples + /// RustStr -> &str + /// *mut RustString -> String + /// FfiSlice -> &[u8] + fn convert_ffi_expression_to_rust_type( + &self, + expression: &TokenStream, + span: Span, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// Convert an Option FFI representation to the Rust representation. + fn convert_ffi_option_expression_to_rust_type(&self, expression: &TokenStream) -> TokenStream; + + /// Convert an FFI expression to this type's Swift representation. + fn convert_ffi_expression_to_swift_type( + &self, + expression: &str, + type_pos: TypePosition, + types: &TypeDeclarations, + ) -> String; + + /// Convert an Option FFI representation to the Rust representation. + fn convert_ffi_option_expression_to_swift_type(&self, expression: &str) -> String; + + /// Convert an FFI Result::Ok value to Rust value. + /// + /// For example, for `Result` this would convert + /// `swift_bridge::result::ResultPtrAndPtr.ok_or_err` into a `String`. + fn convert_ffi_result_ok_value_to_rust_value( + &self, + ok_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// Convert an FFI Result::Err value to Rust value. + /// + /// For example, for `Result<(), String>` this would convert + /// `swift_bridge::result::ResultPtrAndPtr.ok_or_err` into a `String`. + fn convert_ffi_result_err_value_to_rust_value( + &self, + err_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream; + + /// The value used to represent `Option::None` over FFI. + fn unused_option_none_val(&self, swift_bridge_path: &Path) -> UnusedOptionNoneValue; + + /// Whether or not a string can be parsed by this type. + /// + /// # Examples + /// + /// ```ignore + /// assert_eq!(BridgeableVec::token_stream_str_is_of_type("Vec < u8 >"), true); + /// assert_eq!(BridgeableVec::token_stream_str_is_of_type("u8"), false); + /// ``` + fn can_parse_token_stream_str(tokens: &str) -> bool + where + Self: Sized; + + /// Convert the `Type` into this BridgeableType. + fn from_type(ty: &Type, types: &TypeDeclarations) -> Option + where + Self: Sized; + + /// Parse the stringified token stream into this BridgeableType. + fn parse_token_stream_str(tokens: &str, types: &TypeDeclarations) -> Option + where + Self: Sized; + + /// Whether or not this is the null type `()`. + /// TODO: This is temporary as we move towards using this trait.. We should look at how + /// this is being used and create a trait method(s) that handles that particular case instead + /// of checking the type. + fn is_null(&self) -> bool; + + /// Whether or not this is a `str`. + /// TODO: This is temporary as we move towards using this trait.. We should look at how + /// this is being used and create a trait method(s) that handles that particular case instead + /// of checking the type. + fn is_str(&self) -> bool; + + /// Whether or not the type is a `String`, or a type that contains an owned String such as + /// `Option` or `struct Foo { field: String }` + /// TODO: This is temporary as we move towards using this trait.. We should look at how + /// this is being used and create a trait method(s) that handles that particular case instead + /// of checking the type. + fn contains_owned_string_recursive(&self) -> bool; + + /// Whether or not the type is a `&str`, or a type that contains a &str such as + /// `Option<&str>` or `struct Foo { field: &'static str } ` + /// TODO: This is temporary as we move towards using this trait.. We should look at how + /// this is being used and create a trait method(s) that handles that particular case instead + /// of checking the type. + fn contains_ref_string_recursive(&self) -> bool; + + /// Parse the type from a `FnArg`. + fn from_fn_arg( + fn_arg: &FnArg, + _associated_type: &Option, + types: &TypeDeclarations, + ) -> Option + where + Self: Sized, + { + match fn_arg { + FnArg::Receiver(_) => { + // + todo!() + } + FnArg::Typed(ty) => Self::from_type(ty.ty.deref(), types), + } + } + + /// Whether or not this type is annotated with `#[swift_bridge(Copy(..))]` + fn has_swift_bridge_copy_annotation(&self) -> bool; +} + +/// Parse a BridgeableType from a stringified token stream. +pub(crate) fn bridgeable_type_from_token_stream_str( + tokens: &str, + types: &TypeDeclarations, +) -> Option> { + // TODO: Try all types before falling back to opaque types below + + if BridgedString::can_parse_token_stream_str(tokens) { + return BridgedString::parse_token_stream_str(tokens, types).map(|o| Box::new(o) as _); + } + + OpaqueForeignType::parse_token_stream_str(tokens, types).map(|o| Box::new(o) as _) +} + +/// Parse a BridgeableType from a stringified token stream. +pub(crate) fn bridgeable_type_from_fn_arg( + fn_arg: &FnArg, + types: &TypeDeclarations, +) -> Option> { + match fn_arg { + FnArg::Receiver(_) => None, + FnArg::Typed(pat_ty) => { + BridgedType::new_with_type(&pat_ty.ty, types).map(|o| Box::new(o) as _) + } + } +} + +// TODO: We're replacing this with `Box`. +// So continue to move more functionality into that trait. +#[derive(Debug)] pub(crate) enum BridgedType { StdLib(StdLibType), Foreign(CustomBridgedType), + // TODO: Move all of the Self::StdLib and Self::Foreign variants into here.. then we can + // delete BridgedType entirely and just use `Box` everywhere. + Bridgeable(Box), } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq)] pub(crate) enum CustomBridgedType { Shared(SharedType), - Opaque(OpaqueForeignType), } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] pub(crate) enum StdLibType { Null, // TODO: Move the ints, floats, and bool types into enum StdLibPrimitive since they tend @@ -60,9 +315,8 @@ pub(crate) enum StdLibType { RefSlice(BuiltInRefSlice), /// &str Str, - String, Vec(BuiltInVec), - BoxedFnOnce(BuiltInBoxedFnOnce), + BoxedFnOnce(BridgeableBoxedFnOnce), Option(BridgedOption), Result(BuiltInResult), } @@ -77,199 +331,30 @@ pub(crate) enum TypePosition { SwiftCallsRustAsyncOnCompleteReturnTy, } -#[derive(Debug, PartialEq, Clone)] -pub(crate) struct BuiltInPointer { - pub kind: PointerKind, - pub pointee: Pointee, -} - -/// The target of an `*const` or `*mut` pointer. -#[derive(Clone)] -pub(crate) enum Pointee { - BuiltIn(Box), - /// `*const SomeType` - /// ^^^^^^^^ This is the Pointee - Void(Type), -} - -impl ToTokens for Pointee { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Pointee::BuiltIn(built_in) => { - built_in.to_rust_type_path().to_tokens(tokens); - } - Pointee::Void(ty) => { - ty.to_tokens(tokens); - } - }; - } -} - -impl Debug for Pointee { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Pointee::BuiltIn(built_in) => f.debug_tuple("BuiltIn").field(&built_in).finish(), - Pointee::Void(ty) => f - .debug_tuple("Void") - .field(&ty.to_token_stream().to_string()) - .finish(), - } - } -} - -impl PartialEq for Pointee { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::BuiltIn(left), Self::BuiltIn(right)) => left == right, - (Self::Void(left), Self::Void(right)) => { - left.to_token_stream().to_string() == right.to_token_stream().to_string() - } - _ => false, - } - } -} - /// &[T] -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] pub(crate) struct BuiltInRefSlice { pub ty: Box, } /// Vec -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] pub(crate) struct BuiltInVec { pub ty: Box, } -#[derive(Debug, PartialEq, Clone)] -pub(crate) enum PointerKind { - Const, - Mut, -} - -impl ToTokens for PointerKind { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - PointerKind::Const => { - let t = quote! { *const }; - t.to_tokens(tokens); - } - PointerKind::Mut => { - let t = quote! { *mut }; - t.to_tokens(tokens); - } - } - } -} - impl BridgedType { pub fn is_null(&self) -> bool { matches!(self, BridgedType::StdLib(StdLibType::Null)) } } -#[cfg(test)] -impl BridgedType { - fn unwrap_stdlib(&self) -> &StdLibType { - match self { - BridgedType::StdLib(s) => s, - _ => panic!(), - } - } -} - #[derive(Debug, PartialEq, Clone)] pub(crate) enum SharedType { Struct(SharedStruct), Enum(SharedEnum), } -#[derive(Clone)] -pub(crate) struct OpaqueForeignType { - pub ty: Ident, - pub host_lang: HostLang, - pub reference: bool, - pub mutable: bool, - pub has_swift_bridge_copy_annotation: bool, - pub generics: OpaqueRustTypeGenerics, -} - -impl OpaqueForeignType { - fn swift_name(&self) -> String { - format!("{}", self.ty) - } - - /// The name of the type used to pass a `#[swift_bridge(Copy(...))]` type over FFI - /// - /// __swift_bridge__SomeType - fn copy_rust_repr_type(&self) -> Ident { - let ty = format!( - "{}{}{}", - SWIFT_BRIDGE_PREFIX, - self.ty, - self.generics.underscore_prefixed_generics_string() - ); - Ident::new(&ty, self.ty.span()) - } - - /// The name of the type used to pass a `Option` where T is - /// `#[swift_bridge(Copy(...))]` over FFI - /// - /// __swift_bridge__Option_SomeType - fn option_copy_rust_repr_type(&self) -> Ident { - let ty = format!( - "{}Option_{}{}", - SWIFT_BRIDGE_PREFIX, - self.ty, - self.generics.underscore_prefixed_generics_string() - ); - Ident::new(&ty, self.ty.span()) - } - - /// The FFI name of the type used to pass a `Option` where T is - /// `#[swift_bridge(Copy(...))]` over FFI - /// - /// __swift_bridge__$Option$SomeType - fn option_copy_ffi_repr_type_string(&self) -> String { - format!( - "{}$Option${}{}", - SWIFT_BRIDGE_PREFIX, - self.ty, - self.generics.dollar_prefixed_generics_string() - ) - } - - /// The name of the type used to pass a `#[swift_bridge(Copy(...))]` type over FFI - fn copy_ffi_repr_type_string(&self) -> String { - format!( - "{}${}{}", - SWIFT_BRIDGE_PREFIX, - self.ty, - self.generics.dollar_prefixed_generics_string() - ) - } -} - -impl Debug for OpaqueForeignType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("OpaqueForeignType") - .field("ty", &self.ty.to_token_stream()) - .field("host_lang", &self.host_lang) - .field("reference", &self.reference) - .field("mutable", &self.mutable) - .finish() - } -} - -impl PartialEq for OpaqueForeignType { - fn eq(&self, other: &Self) -> bool { - self.ty.to_token_stream().to_string() == other.ty.to_token_stream().to_string() - && self.host_lang == other.host_lang - && self.reference == other.reference - && self.mutable == other.mutable - } -} - /// Whether or not a PatType's pattern is `self`. /// /// `self: &Foo` would be true @@ -294,11 +379,182 @@ pub(crate) fn fn_arg_name(fn_arg: &FnArg) -> Option<&Ident> { } } -impl Deref for OpaqueForeignType { - type Target = Ident; +impl BridgeableType for BridgedType { + fn is_built_in_type(&self) -> bool { + !self.is_custom_type() + } + + fn to_rust_type_path(&self) -> TokenStream { + self.to_rust_type_path() + } + + fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + self.to_swift_type(type_pos, types) + } + + fn to_c_type(&self) -> String { + self.to_c() + } + + fn to_c_include(&self) -> Option<&'static str> { + todo!() + } - fn deref(&self) -> &Self::Target { - &self.ty + fn to_ffi_compatible_rust_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + self.to_ffi_compatible_rust_type(swift_bridge_path, types) + } + + fn to_ffi_compatible_option_rust_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn to_ffi_compatible_option_swift_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> String { + todo!() + } + + fn to_ffi_compatible_option_c_type(&self) -> String { + todo!() + } + + fn convert_rust_expression_to_ffi_type( + &self, + _expression: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn convert_option_rust_expression_to_ffi_type( + &self, + _expression: &TokenStream, + _swift_bridge_path: &Path, + ) -> TokenStream { + todo!() + } + + fn convert_swift_expression_to_ffi_type( + &self, + expression: &str, + type_pos: TypePosition, + ) -> String { + self.convert_swift_expression_to_ffi_type(expression, type_pos) + } + + fn convert_option_swift_expression_to_ffi_type( + &self, + _expression: &str, + _type_pos: TypePosition, + ) -> String { + todo!() + } + + fn convert_ffi_expression_to_rust_type( + &self, + _expression: &TokenStream, + _span: Span, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + todo!() + } + + fn convert_ffi_option_expression_to_rust_type(&self, _expression: &TokenStream) -> TokenStream { + todo!() + } + + fn convert_ffi_expression_to_swift_type( + &self, + expression: &str, + type_pos: TypePosition, + types: &TypeDeclarations, + ) -> String { + self.convert_ffi_value_to_swift_value(expression, type_pos, types) + } + + fn convert_ffi_option_expression_to_swift_type(&self, _expression: &str) -> String { + todo!() + } + + fn convert_ffi_result_ok_value_to_rust_value( + &self, + ok_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + self.convert_ffi_result_ok_value_to_rust_value(ok_ffi_value, swift_bridge_path, types) + } + + fn convert_ffi_result_err_value_to_rust_value( + &self, + err_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + self.convert_ffi_result_err_value_to_rust_value(err_ffi_value, swift_bridge_path, types) + } + + fn unused_option_none_val(&self, swift_bridge_path: &Path) -> UnusedOptionNoneValue { + self.unused_option_none_val(swift_bridge_path) + } + + fn can_parse_token_stream_str(_tokens: &str) -> bool + where + Self: Sized, + { + true + } + + fn from_type(_ty: &Type, _types: &TypeDeclarations) -> Option + where + Self: Sized, + { + todo!() + } + + fn parse_token_stream_str(_tokens: &str, _types: &TypeDeclarations) -> Option + where + Self: Sized, + { + todo!() + } + + fn is_null(&self) -> bool { + self.is_null() + } + + fn is_str(&self) -> bool { + match self { + BridgedType::StdLib(StdLibType::Str) => true, + _ => false, + } + } + + fn contains_owned_string_recursive(&self) -> bool { + self.contains_owned_string_recursive() + } + + fn contains_ref_string_recursive(&self) -> bool { + todo!() + } + + fn has_swift_bridge_copy_annotation(&self) -> bool { + match self { + BridgedType::Bridgeable(b) => b.has_swift_bridge_copy_annotation(), + _ => false, + } } } @@ -374,12 +630,12 @@ impl BridgedType { } } - pub fn new_with_str(string: &str, types: &TypeDeclarations) -> Option { - let string = string.replace("\n", " "); - let string = string.as_str(); + pub fn new_with_str(tokens: &str, types: &TypeDeclarations) -> Option { + let tokens = tokens.replace("\n", " "); + let tokens = tokens.as_str(); - if string.starts_with("Vec < ") { - let inner = string.trim_start_matches("Vec < "); + if tokens.starts_with("Vec < ") { + let inner = tokens.trim_start_matches("Vec < "); let inner = inner.trim_end_matches(" >"); let inner = if let Some(declared_ty) = types.get(inner) { @@ -392,10 +648,10 @@ impl BridgedType { return Some(BridgedType::StdLib(StdLibType::Vec(BuiltInVec { ty: Box::new(inner), }))); - } else if string.starts_with("Option < ") { - let last_bracket = string.rfind(">")?; + } else if tokens.starts_with("Option < ") { + let last_bracket = tokens.rfind(">")?; - let inner = &string[0..last_bracket]; + let inner = &tokens[0..last_bracket]; let inner = inner.trim_start_matches("Option < "); // Remove spaces from generics. i.e. "SomeType < u32 > " -> "SomeType" @@ -411,17 +667,17 @@ impl BridgedType { return Some(BridgedType::StdLib(StdLibType::Option(BridgedOption { ty: Box::new(inner), }))); - } else if string.starts_with("Result < ") { + } else if tokens.starts_with("Result < ") { return Some(BridgedType::StdLib(StdLibType::Result( - BuiltInResult::from_str_tokens(&string, types)?, + BuiltInResult::from_str_tokens(&tokens, types)?, ))); - } else if string.starts_with("Box < dyn FnOnce") { + } else if tokens.starts_with("Box < dyn FnOnce") { return Some(BridgedType::StdLib(StdLibType::BoxedFnOnce( - BuiltInBoxedFnOnce::from_str_tokens(&string, types)?, + BridgeableBoxedFnOnce::from_str_tokens(&tokens, types)?, ))); } - let ty = match string { + let ty = match tokens { "u8" => BridgedType::StdLib(StdLibType::U8), "i8" => BridgedType::StdLib(StdLibType::I8), "u16" => BridgedType::StdLib(StdLibType::U16), @@ -434,11 +690,14 @@ impl BridgedType { "isize" => BridgedType::StdLib(StdLibType::Isize), "f32" => BridgedType::StdLib(StdLibType::F32), "f64" => BridgedType::StdLib(StdLibType::F64), - "String" => BridgedType::StdLib(StdLibType::String), "bool" => BridgedType::StdLib(StdLibType::Bool), "()" => BridgedType::StdLib(StdLibType::Null), _ => { - let bridged_type = types.get(string)?; + if let Some(b) = bridgeable_type_from_token_stream_str(tokens, types) { + return Some(BridgedType::Bridgeable(b)); + } + + let bridged_type = types.get(tokens)?; let bridged_type = bridged_type.to_bridged_type(false, false); bridged_type } @@ -452,6 +711,7 @@ impl BridgedType { // SomeOpaqueRustType -> super::SomeOpaqueRustType pub(crate) fn to_rust_type_path(&self) -> TokenStream { match self { + BridgedType::Bridgeable(b) => b.to_rust_type_path(), BridgedType::StdLib(stdlib_type) => { match stdlib_type { StdLibType::Null => { @@ -489,7 +749,6 @@ impl BridgedType { quote! { &[#ty]} } StdLibType::Str => quote! { &str }, - StdLibType::String => quote! { String }, StdLibType::Vec(v) => { let ty = v.ty.to_rust_type_path(); quote! { Vec<#ty> } @@ -512,19 +771,6 @@ impl BridgedType { // todo!("Shared enum to Rust type name") } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let ty_name = &opaque.ty; - - if opaque.host_lang.is_rust() { - quote! { - super:: #ty_name - } - } else { - quote! { - #ty_name - } - } - } } } @@ -539,6 +785,7 @@ impl BridgedType { types: &TypeDeclarations, ) -> TokenStream { let ty = match self { + BridgedType::Bridgeable(b) => b.to_ffi_compatible_rust_type(swift_bridge_path, types), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::U8 => quote! {u8}, StdLibType::I8 => quote! { i8 }, @@ -579,14 +826,14 @@ impl BridgedType { StdLibType::Null => { quote! { () } } - StdLibType::String => { - quote! { *mut #swift_bridge_path::string::RustString } - } StdLibType::Vec(ty) => { let ty = ty.ty.to_rust_type_path(); quote! { *mut Vec<#ty> } } StdLibType::Option(opt) => match opt.ty.deref() { + BridgedType::Bridgeable(b) => { + b.to_ffi_compatible_option_rust_type(swift_bridge_path, types) + } BridgedType::StdLib(stdlib_ty) => match stdlib_ty { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -639,9 +886,6 @@ impl BridgedType { StdLibType::Str => { quote! { #swift_bridge_path::string::RustStr } } - StdLibType::String => { - opt.ty.to_ffi_compatible_rust_type(swift_bridge_path, types) - } StdLibType::Vec(_) => { todo!("Option> is not yet supported") } @@ -667,19 +911,6 @@ impl BridgedType { let name = shared_enum.ffi_option_name_tokens(); quote! { #name } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let type_name = &opaque.ty; - - if opaque.has_swift_bridge_copy_annotation { - let option_ty = opaque.option_copy_rust_repr_type(); - quote! { #option_ty } - } else { - let generics = opaque - .generics - .angle_bracketed_concrete_generics_tokens(types); - quote! { *mut super::#type_name #generics } - } - } }, StdLibType::Result(result) => result.to_ffi_compatible_rust_type(swift_bridge_path), StdLibType::BoxedFnOnce(fn_once) => fn_once.to_ffi_compatible_rust_type(), @@ -704,34 +935,6 @@ impl BridgedType { quote! { #ffi_ty_name } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let ty_name = &opaque.ty; - - if opaque.has_swift_bridge_copy_annotation { - let ty = opaque.copy_rust_repr_type(); - quote! { #ty } - } else { - if opaque.host_lang.is_rust() { - let generics = opaque - .generics - .angle_bracketed_concrete_generics_tokens(types); - - if opaque.reference { - let ptr = if opaque.mutable { - quote! { *mut } - } else { - quote! { *const } - }; - - quote_spanned! {ty_name.span()=> #ptr super::#ty_name } - } else { - quote! { *mut super::#ty_name #generics } - } - } else { - quote! { #ty_name } - } - } - } }; quote!(#ty) @@ -742,6 +945,7 @@ impl BridgedType { // ... etc pub fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { match self { + BridgedType::Bridgeable(b) => b.to_swift_type(type_pos, types), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::U8 => "UInt8".to_string(), StdLibType::I8 => "Int8".to_string(), @@ -812,14 +1016,6 @@ impl BridgedType { unimplemented!() } }, - StdLibType::String => match type_pos { - TypePosition::FnArg(_func_host_lang, _) => "GenericIntoRustString".to_string(), - TypePosition::FnReturn(_func_host_lang) => "RustString".to_string(), - TypePosition::SharedStructField => "RustString".to_string(), - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - "UnsafeMutableRawPointer?".to_string() - } - }, StdLibType::Vec(ty) => { format!("RustVec<{}>", ty.ty.to_swift_type(type_pos, types)) } @@ -874,68 +1070,12 @@ impl BridgedType { } } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.host_lang.is_rust() { - match type_pos { - TypePosition::FnArg(func_host_lang, _) - | TypePosition::FnReturn(func_host_lang) => { - if func_host_lang.is_rust() { - let mut class_name = opaque.ty.to_string(); - - if !opaque.has_swift_bridge_copy_annotation { - if opaque.reference { - class_name += "Ref"; - } - - if opaque.mutable { - class_name += "Mut"; - } - } - - format!( - "{}{}", - class_name, - opaque - .generics - .angle_bracketed_generic_concrete_swift_types_string(types) - ) - } else { - format!("UnsafeMutableRawPointer") - } - } - TypePosition::SharedStructField => { - // - unimplemented!() - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - unimplemented!() - } - } - } else { - match type_pos { - TypePosition::FnArg(func_host_lang, _) - | TypePosition::FnReturn(func_host_lang) => { - if func_host_lang.is_rust() { - opaque.ty.to_string() - } else { - "__private__PointerToSwiftType".to_string() - } - } - TypePosition::SharedStructField => { - // - unimplemented!() - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - unimplemented!() - } - } - } - } } } pub fn to_c(&self) -> String { match self { + BridgedType::Bridgeable(b) => b.to_c_type(), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::U8 => "uint8_t".to_string(), StdLibType::I8 => "int8_t".to_string(), @@ -966,7 +1106,6 @@ impl BridgedType { StdLibType::RefSlice(_slice) => "struct __private__FfiSlice".to_string(), StdLibType::Str => "struct RustStr".to_string(), StdLibType::Null => "void".to_string(), - StdLibType::String => "void*".to_string(), StdLibType::Vec(_) => "void*".to_string(), StdLibType::Option(opt) => opt.to_c(), StdLibType::Result(result) => result.to_c().to_string(), @@ -978,17 +1117,6 @@ impl BridgedType { BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(shared_enum))) => { format!("struct {}", shared_enum.ffi_name_string()) } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.host_lang.is_rust() { - if opaque.has_swift_bridge_copy_annotation { - format!("struct {}", opaque.copy_ffi_repr_type_string()) - } else { - "void*".to_string() - } - } else { - "struct __private__PointerToSwiftType".to_string() - } - } } } @@ -1041,13 +1169,16 @@ impl BridgedType { // Examples: // If value foo is a String.. `foo` becomes `swiftbridge:string::RustString(foo)` // If value bar is a &str.. `bar` becomes `swiftbridge::string::RustStr::from_str(bar)` - pub fn convert_rust_value_to_ffi_compatible_value( + pub fn convert_rust_expression_to_ffi_type( &self, expression: &TokenStream, swift_bridge_path: &Path, types: &TypeDeclarations, ) -> TokenStream { match self { + BridgedType::Bridgeable(b) => { + b.convert_rust_expression_to_ffi_type(expression, swift_bridge_path, types) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null | StdLibType::U8 @@ -1080,16 +1211,11 @@ impl BridgedType { #swift_bridge_path::string::RustStr::from_str( #expression ) } } - StdLibType::String => { - quote! { - #swift_bridge_path::string::RustString( #expression ).box_into_raw() - } - } StdLibType::Vec(_) => { quote! { Box::into_raw(Box::new( #expression )) } } StdLibType::Option(opt) => { - opt.convert_rust_value_to_ffi_value(expression, swift_bridge_path) + opt.convert_rust_expression_to_ffi_type(expression, swift_bridge_path) } StdLibType::Result(_) => { todo!("Result is not yet supported") @@ -1108,39 +1234,6 @@ impl BridgedType { #expression.into_ffi_repr() } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let ty_name = &opaque.ty; - - if opaque.host_lang.is_rust() { - if opaque.has_swift_bridge_copy_annotation { - let copy_ty = opaque.copy_rust_repr_type(); - quote! { - #copy_ty::from_rust_repr(#expression) - } - } else if opaque.reference { - let ptr = if opaque.mutable { - quote! { *mut } - } else { - quote! { *const } - }; - - quote! { - #expression as #ptr super::#ty_name - } - } else { - let generics = opaque - .generics - .angle_bracketed_concrete_generics_tokens(types); - quote! { - Box::into_raw(Box::new(#expression)) as *mut super::#ty_name #generics - } - } - } else { - quote! { - #expression - } - } - } } } @@ -1150,7 +1243,7 @@ impl BridgedType { // RustStr -> &str // *mut RustString -> String // FfiSlice -> &[u8] - pub fn convert_ffi_value_to_rust_value( + pub fn convert_ffi_expression_to_rust_type( &self, value: &TokenStream, span: Span, @@ -1158,6 +1251,9 @@ impl BridgedType { types: &TypeDeclarations, ) -> TokenStream { match self { + BridgedType::Bridgeable(b) => { + b.convert_ffi_expression_to_rust_type(value, span, swift_bridge_path, types) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null | StdLibType::U8 @@ -1184,18 +1280,13 @@ impl BridgedType { StdLibType::Str => { quote_spanned! {span=> #value.to_str() } } - StdLibType::String => { - quote_spanned! {span=> - unsafe { Box::from_raw(#value).0 } - } - } StdLibType::Vec(_) => { quote_spanned! {span=> unsafe { * Box::from_raw(#value) } } } StdLibType::Option(bridged_option) => { - bridged_option.convert_ffi_value_to_rust_value(value) + bridged_option.convert_ffi_expression_to_rust_type(value) } StdLibType::Result(result) => { result.convert_ffi_value_to_rust_value(value, span, swift_bridge_path, types) @@ -1214,42 +1305,6 @@ impl BridgedType { #value.into_rust_repr() } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.host_lang.is_rust() { - if opaque.has_swift_bridge_copy_annotation { - let maybe_ref = if opaque.reference { - quote! {&} - } else { - quote! {} - }; - quote! { - #maybe_ref #value.into_rust_repr() - } - } else if opaque.reference { - let maybe_mut = if opaque.mutable { - quote! { mut } - } else { - quote! {} - }; - - quote! { - unsafe { & #maybe_mut * #value } - } - } else { - quote! { - unsafe { * Box::from_raw( #value ) } - } - } - } else { - if opaque.reference { - todo!("Handle referenced opaque Swift types") - } else { - quote! { - #value - } - } - } - } } } @@ -1269,11 +1324,14 @@ impl BridgedType { // `__swift_bridge__$create_string(str)` to `RustString(ptr: __swift_bridge__$create_string(str))` pub fn convert_ffi_value_to_swift_value( &self, - value: &str, + expression: &str, type_pos: TypePosition, types: &TypeDeclarations, ) -> String { match self { + BridgedType::Bridgeable(b) => { + b.convert_ffi_expression_to_swift_type(expression, type_pos, types) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null | StdLibType::U8 @@ -1288,53 +1346,43 @@ impl BridgedType { | StdLibType::Isize | StdLibType::F32 | StdLibType::F64 - | StdLibType::Bool => value.to_string(), + | StdLibType::Bool => expression.to_string(), StdLibType::Pointer(ptr) => match &ptr.pointee { - Pointee::BuiltIn(_) => value.to_string(), + Pointee::BuiltIn(_) => expression.to_string(), Pointee::Void(_ty) => match ptr.kind { PointerKind::Const => match type_pos { TypePosition::FnArg(func_host_lang, _) => { if func_host_lang.is_rust() { - format!("UnsafeRawPointer({}!)", value) + format!("UnsafeRawPointer({}!)", expression) } else { - value.to_string() + expression.to_string() } } TypePosition::FnReturn(_) => { - format!("UnsafeRawPointer({}!)", value) + format!("UnsafeRawPointer({}!)", expression) } TypePosition::SharedStructField => { - format!("UnsafeRawPointer({}!)", value) + format!("UnsafeRawPointer({}!)", expression) } TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { unimplemented!() } }, - PointerKind::Mut => value.to_string(), + PointerKind::Mut => expression.to_string(), }, }, StdLibType::RefSlice(ty) => { format!( - "let slice = {value}; return UnsafeBufferPointer(start: slice.start.assumingMemoryBound(to: {ty}.self), count: Int(slice.len));", - value = value, - ty = ty.ty.to_swift_type(type_pos,types) + "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) ) } - StdLibType::Str => value.to_string(), - StdLibType::String => match type_pos { - TypePosition::FnArg(_, _) - | TypePosition::FnReturn(_) - | TypePosition::SharedStructField => { - format!("RustString(ptr: {})", value) - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - format!("RustString(ptr: {}!)", value) - } - }, + StdLibType::Str => expression.to_string(), StdLibType::Vec(_ty) => { - format!("RustVec(ptr: {})", value) + format!("RustVec(ptr: {})", expression) } - StdLibType::Option(opt) => opt.convert_ffi_expression_to_swift(value), + StdLibType::Option(opt) => opt.convert_ffi_expression_to_swift_type(expression), StdLibType::Result(_) => { todo!("Result is not yet supported") } @@ -1343,38 +1391,10 @@ impl BridgedType { } }, BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(_shared_struct))) => { - format!("{}.intoSwiftRepr()", value) + format!("{}.intoSwiftRepr()", expression) } BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(_shared_enum))) => { - format!("{}.intoSwiftRepr()", value) - } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let mut ty_name = opaque.ty.to_string(); - - if opaque.reference { - ty_name += "Ref"; - } - if opaque.mutable { - ty_name += "Mut"; - } - - if opaque.host_lang.is_rust() { - if opaque.has_swift_bridge_copy_annotation { - format!( - "{ty_name}(bytes: {value})", - ty_name = ty_name, - value = value, - ) - } else { - format!("{ty_name}(ptr: {value})", ty_name = ty_name, value = value,) - } - } else { - format!( - "Unmanaged<{ty_name}>.fromOpaque({value}.ptr).takeRetainedValue()", - ty_name = ty_name, - value = value - ) - } + format!("{}.intoSwiftRepr()", expression) } } } @@ -1386,12 +1406,15 @@ impl BridgedType { /// If `value: UnsafeBufferPoint` then `value` becomes: `value.toFfiSlice()`, /// where `.toFfiSlice()` is a function that creates a `__private__FfiSlice` which is /// C compatible. - pub fn convert_swift_expression_to_ffi_compatible( + pub fn convert_swift_expression_to_ffi_type( &self, - value: &str, + expression: &str, type_pos: TypePosition, ) -> String { match self { + BridgedType::Bridgeable(b) => { + b.convert_swift_expression_to_ffi_type(expression, type_pos) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null | StdLibType::U8 @@ -1406,19 +1429,19 @@ impl BridgedType { | StdLibType::Isize | StdLibType::F32 | StdLibType::F64 - | StdLibType::Bool => value.to_string(), + | StdLibType::Bool => expression.to_string(), StdLibType::RefSlice(_) => { - format!("{}.toFfiSlice()", value) + format!("{}.toFfiSlice()", expression) } StdLibType::Pointer(ptr) => match &ptr.pointee { - Pointee::BuiltIn(_) => value.to_string(), + Pointee::BuiltIn(_) => expression.to_string(), Pointee::Void(_ty) => match type_pos { TypePosition::FnArg(func_host_lang, _) | TypePosition::FnReturn(func_host_lang) => { if ptr.kind == PointerKind::Const && func_host_lang.is_rust() { - format!("UnsafeMutableRawPointer(mutating: {})", value) + format!("UnsafeMutableRawPointer(mutating: {})", expression) } else { - value.to_string() + expression.to_string() } } TypePosition::SharedStructField => { @@ -1433,9 +1456,9 @@ impl BridgedType { TypePosition::FnArg(func_host_lang, _) | TypePosition::FnReturn(func_host_lang) => { if func_host_lang.is_rust() { - format!("{val}AsRustStr", val = value) + format!("{val}AsRustStr", val = expression) } else { - value.to_string() + expression.to_string() } } TypePosition::SharedStructField => { @@ -1445,103 +1468,64 @@ impl BridgedType { unimplemented!() } }, - StdLibType::String => { - format!( - "{{ let rustString = {value}.intoRustString(); rustString.isOwned = false; return rustString.ptr }}()", - value = value - ) - } StdLibType::Vec(_) => { format!( "{{ let val = {value}; val.isOwned = false; return val.ptr }}()", - value = value + value = expression ) } StdLibType::Option(option) => { - option.convert_swift_expression_to_ffi_compatible(value, type_pos) + option.convert_swift_expression_to_ffi_type(expression, type_pos) } StdLibType::Result(result) => { - result.convert_swift_expression_to_ffi_compatible(value, type_pos) + result.convert_swift_expression_to_ffi_compatible(expression, type_pos) } StdLibType::BoxedFnOnce(_) => { todo!("Support Box C>") } }, BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(_shared_struct))) => { - format!("{}.intoFfiRepr()", value) + format!("{}.intoFfiRepr()", expression) } BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(_shared_enum))) => { - format!("{}.intoFfiRepr()", value) + format!("{}.intoFfiRepr()", expression) } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let ty_name = &opaque.ty; - - if opaque.host_lang.is_rust() { - if opaque.has_swift_bridge_copy_annotation { - format!("{}.intoFfiRepr()", value) - } else if opaque.reference { - format!("{}.ptr", value) - } else { - match type_pos { - TypePosition::FnArg(func_host_lang, _) - | TypePosition::FnReturn(func_host_lang) => { - if func_host_lang.is_rust() { - format!( - "{{{}.isOwned = false; return {}.ptr;}}()", - value, value - ) - } else { - format!( - "{type_name}(ptr: {value})", - type_name = ty_name, - value = value - ) - } - } - TypePosition::SharedStructField => { - todo!("Opaque types in shared struct fields are not yet supported") - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - unimplemented!() - } - } - } - } else { - match type_pos { - TypePosition::FnArg(func_host_lang, _) => { - if func_host_lang.is_rust() { - format!( - "__private__PointerToSwiftType(ptr: Unmanaged.passRetained({}).toOpaque())", - value - ) - } else { - format!( - "Unmanaged<{type_name}>.fromOpaque({value}.ptr).takeRetainedValue()", - type_name = ty_name, - value = value - ) - } - } - TypePosition::FnReturn(_func_host_lang) => { - format!( - "__private__PointerToSwiftType(ptr: Unmanaged.passRetained({}).toOpaque())", - value - ) - } - TypePosition::SharedStructField => { - todo!("Opaque types in shared struct fields are not yet supported") - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - unimplemented!() - } - } - } + } + } + + fn convert_ffi_result_ok_value_to_rust_value( + &self, + ok_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + match self { + BridgedType::Bridgeable(b) => { + b.convert_ffi_result_ok_value_to_rust_value(ok_ffi_value, swift_bridge_path, types) } + _ => unimplemented!(), + } + } + + fn convert_ffi_result_err_value_to_rust_value( + &self, + err_ffi_value: &TokenStream, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + match self { + BridgedType::Bridgeable(b) => b.convert_ffi_result_err_value_to_rust_value( + err_ffi_value, + swift_bridge_path, + types, + ), + _ => unimplemented!(), } } - pub fn c_include(&self) -> Option<&'static str> { + pub fn to_c_include(&self) -> Option<&'static str> { match self { + BridgedType::Bridgeable(b) => b.to_c_include(), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::U8 | StdLibType::I8 @@ -1555,10 +1539,10 @@ impl BridgedType { | StdLibType::Isize => Some("stdint.h"), StdLibType::Bool => Some("stdbool.h"), StdLibType::Pointer(ptr) => match &ptr.pointee { - Pointee::BuiltIn(ty) => ty.c_include(), + Pointee::BuiltIn(ty) => ty.to_c_include(), Pointee::Void(_) => None, }, - StdLibType::RefSlice(slice) => slice.ty.c_include(), + StdLibType::RefSlice(slice) => slice.ty.to_c_include(), StdLibType::Vec(_vec) => Some("stdint.h"), _ => None, }, @@ -1570,14 +1554,14 @@ impl BridgedType { // TODO: Iterate over the fields and see if any of them need imports.. None } - BridgedType::Foreign(CustomBridgedType::Opaque(_opaque)) => None, } } /// When we want to return an Option::None we still need to return a dummy value to appease the /// type checker, even though it never gets used by the caller. - fn rust_unused_option_none_val(&self, swift_bridge_path: &Path) -> UnusedOptionNoneValue { + fn unused_option_none_val(&self, swift_bridge_path: &Path) -> UnusedOptionNoneValue { match self { + BridgedType::Bridgeable(b) => b.unused_option_none_val(swift_bridge_path), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => UnusedOptionNoneValue { rust: quote! { () }, @@ -1621,17 +1605,6 @@ impl BridgedType { swift: "TODO_SWIFT_OPTIONAL_STR_SUPPORT".to_string(), } } - StdLibType::String => { - UnusedOptionNoneValue { - rust: quote! { - std::ptr::null::<#swift_bridge_path::string::RustString>() as *mut #swift_bridge_path::string::RustString - }, - // TODO: Add integration tests: - // Rust: crates/swift-integration-tests/src/option.rs - // Swift: OptionTests.swift - swift: "TODO_SWIFT_OPTIONAL_STRING_SUPPORT".to_string(), - } - } StdLibType::Vec(_) => { todo!("Support Option>") } @@ -1659,18 +1632,6 @@ impl BridgedType { swift: "TODO..Support Swift Option::None value".into(), } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - let ty_name = &opaque.ty; - - if opaque.reference { - todo!("Support returning Option<&T> where T is an opaque type") - } else { - UnusedOptionNoneValue { - rust: quote! { std::ptr::null::<#ty_name>() as *mut super::#ty_name }, - swift: "TODO..Support Swift Option::None value".into(), - } - } - } } } @@ -1678,8 +1639,8 @@ impl BridgedType { /// `Option` or `struct Foo { field: String } ` pub fn contains_owned_string_recursive(&self) -> bool { match self { + BridgedType::Bridgeable(b) => b.contains_owned_string_recursive(), BridgedType::StdLib(stdlib_type) => match stdlib_type { - StdLibType::String => true, StdLibType::Vec(inner) => inner.ty.contains_owned_string_recursive(), StdLibType::Option(inner) => inner.ty.contains_owned_string_recursive(), StdLibType::Result(inner) => { @@ -1696,7 +1657,6 @@ impl BridgedType { // TODO: Check fields for &str false } - BridgedType::Foreign(CustomBridgedType::Opaque(_)) => false, } } @@ -1704,6 +1664,7 @@ impl BridgedType { /// `Option<&str>` or `struct Foo { field: &'static str } ` pub fn contains_ref_string_recursive(&self) -> bool { match self { + BridgedType::Bridgeable(b) => b.contains_ref_string_recursive(), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Str => true, StdLibType::Vec(inner) => inner.ty.contains_ref_string_recursive(), @@ -1718,7 +1679,6 @@ impl BridgedType { // TODO: Check fields for &str false } - BridgedType::Foreign(CustomBridgedType::Opaque(_)) => false, } } @@ -1751,9 +1711,17 @@ impl BridgedType { } } } + + pub fn is_custom_type(&self) -> bool { + match self { + BridgedType::StdLib(_) => false, + BridgedType::Foreign(_) => true, + BridgedType::Bridgeable(b) => b.is_custom_type(), + } + } } -struct UnusedOptionNoneValue { +pub(crate) struct UnusedOptionNoneValue { rust: TokenStream, #[allow(unused)] swift: String, @@ -1761,76 +1729,9 @@ struct UnusedOptionNoneValue { #[cfg(test)] mod tests { - use quote::quote; - use syn::parse_quote; use super::*; - /// Verify that we can parse std lib types. - #[test] - fn std_lib_types() { - let tests = vec![ - (quote! {u8}, StdLibType::U8), - (quote! {i8}, StdLibType::I8), - (quote! {u16}, StdLibType::U16), - (quote! {i16}, StdLibType::I16), - (quote! {u32}, StdLibType::U32), - (quote! {i32}, StdLibType::I32), - (quote! {u64}, StdLibType::U64), - (quote! {i64}, StdLibType::I64), - (quote! {usize}, StdLibType::Usize), - (quote! {isize}, StdLibType::Isize), - (quote! {f32}, StdLibType::F32), - (quote! {f64}, StdLibType::F64), - (quote! {&str}, StdLibType::Str), - (quote! {String}, StdLibType::String), - ( - quote! { Vec}, - StdLibType::Vec(BuiltInVec { - ty: Box::new(BridgedType::StdLib(StdLibType::U32)), - }), - ), - ( - quote! { Option}, - StdLibType::Option(BridgedOption { - ty: Box::new(BridgedType::StdLib(StdLibType::U32)), - }), - ), - ( - quote! {*const u8}, - StdLibType::Pointer(BuiltInPointer { - kind: PointerKind::Const, - pointee: Pointee::BuiltIn(Box::new(BridgedType::StdLib(StdLibType::U8))), - }), - ), - ( - quote! {*mut f64}, - StdLibType::Pointer(BuiltInPointer { - kind: PointerKind::Mut, - pointee: Pointee::BuiltIn(Box::new(BridgedType::StdLib(StdLibType::F64))), - }), - ), - ( - quote! {*const c_void}, - StdLibType::Pointer(BuiltInPointer { - kind: PointerKind::Const, - pointee: Pointee::Void(syn::parse2(quote! {c_void}).unwrap()), - }), - ), - ]; - for (tokens, expected) in tests { - let ty: Type = parse_quote! {#tokens}; - assert_eq!( - BridgedType::new_with_type(&ty, &TypeDeclarations::default()) - .unwrap() - .unwrap_stdlib(), - &expected, - "{}", - tokens.to_string() - ) - } - } - /// Verify that we treat newline characters as spaces when parsing a type from string. /// Not sure what can lead a stringified token stream to have newline characters in it but /// we've observed it in the wild so this test guards against mishandling that scenario. 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 ffb67e29..689e7d89 100644 --- a/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs +++ b/crates/swift-bridge-ir/src/bridged_type/boxed_fn.rs @@ -11,8 +11,8 @@ use syn::punctuated::Punctuated; use syn::{Path, Type}; /// Box ()> -#[derive(Debug, PartialEq, Clone)] -pub(crate) struct BuiltInBoxedFnOnce { +#[derive(Debug)] +pub(crate) struct BridgeableBoxedFnOnce { /// The functions parameters. pub params: Vec, /// The functions return type. @@ -28,7 +28,7 @@ impl Parse for FunctionArguments { } } -impl BuiltInBoxedFnOnce { +impl BridgeableBoxedFnOnce { pub fn does_not_have_params_or_return(&self) -> bool { self.params.is_empty() && self.ret.is_null() } @@ -128,7 +128,7 @@ impl BuiltInBoxedFnOnce { .enumerate() .map(|(idx, ty)| { let arg_name = Ident::new(&format!("arg{}", idx), Span::call_site()); - ty.convert_ffi_value_to_rust_value( + ty.convert_ffi_expression_to_rust_type( &arg_name.to_token_stream(), arg_name.span(), swift_bridge_path, @@ -164,7 +164,7 @@ impl BuiltInBoxedFnOnce { let arg_name = format!("arg{}", idx); args += &format!( ", {}", - ty.convert_swift_expression_to_ffi_compatible( + ty.convert_swift_expression_to_ffi_type( &arg_name, TypePosition::FnArg(HostLang::Rust, idx) ) @@ -226,7 +226,7 @@ impl BuiltInBoxedFnOnce { } } -impl BuiltInBoxedFnOnce { +impl BridgeableBoxedFnOnce { pub fn from_str_tokens(string: &str, types: &TypeDeclarations) -> Option { // ( A , B , C ) -> D > // OR @@ -262,7 +262,7 @@ impl BuiltInBoxedFnOnce { args_bridged_tys.push(BridgedType::new_with_type(&arg, types)?); } - return Some(BuiltInBoxedFnOnce { + return Some(BridgeableBoxedFnOnce { params: args_bridged_tys, ret: Box::new(ret), }); @@ -279,7 +279,7 @@ mod tests { let tokens = quote! {Box}.to_token_stream().to_string(); assert!( - BuiltInBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) + BridgeableBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) .unwrap() .ret .is_null() @@ -294,7 +294,7 @@ mod tests { .to_string(); assert!(matches!( - *BuiltInBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) + *BridgeableBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) .unwrap() .ret, BridgedType::StdLib(StdLibType::U8) @@ -309,7 +309,7 @@ mod tests { .to_string(); assert!( - BuiltInBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) + BridgeableBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) .unwrap() .ret .is_null(), @@ -325,7 +325,7 @@ mod tests { let tokens = "Box < dyn FnOnce() -> () >"; assert!( - BuiltInBoxedFnOnce::from_str_tokens(tokens, &TypeDeclarations::default()) + BridgeableBoxedFnOnce::from_str_tokens(tokens, &TypeDeclarations::default()) .unwrap() .ret .is_null(), @@ -350,7 +350,7 @@ mod tests { let tokens = test.to_token_stream().to_string(); assert!( - BuiltInBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) + BridgeableBoxedFnOnce::from_str_tokens(&tokens, &TypeDeclarations::default()) .unwrap() .ret .is_null(), diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs new file mode 100644 index 00000000..54b116d0 --- /dev/null +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs @@ -0,0 +1,80 @@ +use crate::bridged_type::BridgedType; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use std::fmt::{Debug, Formatter}; +use syn::Type; + +#[derive(Debug, PartialEq)] +pub(crate) struct BuiltInPointer { + pub kind: PointerKind, + pub pointee: Pointee, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub(crate) enum PointerKind { + Const, + Mut, +} + +/// The target of an `*const` or `*mut` pointer. +pub(crate) enum Pointee { + BuiltIn(Box), + /// `*const SomeType` + /// ^^^^^^^^ This is the Pointee + Void(Type), +} + +impl ToTokens for PointerKind { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + PointerKind::Const => { + let t = quote! { *const }; + t.to_tokens(tokens); + } + PointerKind::Mut => { + let t = quote! { *mut }; + t.to_tokens(tokens); + } + } + } +} + +impl ToTokens for Pointee { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Pointee::BuiltIn(built_in) => { + built_in.to_rust_type_path().to_tokens(tokens); + } + Pointee::Void(ty) => { + ty.to_tokens(tokens); + } + }; + } +} + +impl Debug for Pointee { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Pointee::BuiltIn(built_in) => f.debug_tuple("BuiltIn").field(&built_in).finish(), + Pointee::Void(ty) => f + .debug_tuple("Void") + .field(&ty.to_token_stream().to_string()) + .finish(), + } + } +} + +impl PartialEq for Pointee { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::BuiltIn(_left), Self::BuiltIn(_right)) => { + // left == right + todo!() + } + (Self::Void(left), Self::Void(right)) => { + left.to_token_stream().to_string() == right.to_token_stream().to_string() + } + _ => false, + } + } +} diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_primitive.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_primitive.rs new file mode 100644 index 00000000..39d3cfd9 --- /dev/null +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_primitive.rs @@ -0,0 +1,9 @@ +// /// `impl BridgeableType for {u8, i8, u16, i16 ... etc}` +// TODO: Write this macro +// macro_rules! make_bridgeable_primitive { +// ($type:ty) => { +// impl BridgeableType for $type { +// // +// } +// }; +// } diff --git a/crates/swift-bridge-ir/src/bridged_type/bridged_result.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs similarity index 82% rename from crates/swift-bridge-ir/src/bridged_type/bridged_result.rs rename to crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs index bca4d1a9..6d9d403f 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_result.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_result.rs @@ -1,7 +1,7 @@ -use crate::bridged_type::{BridgedType, TypePosition}; +use crate::bridged_type::{BridgeableType, BridgedType, TypePosition}; use crate::TypeDeclarations; use proc_macro2::{Span, TokenStream}; -use quote::quote; +use quote::{quote, quote_spanned}; use syn::Path; /// Rust: Result @@ -13,10 +13,10 @@ use syn::Path; /// pattern that we use to prevent calling mutable methods on immutable references. /// We only saw this error after `extension: ResultTestOpaqueRustType: Error {}` .. which was /// necessary because Swift's Result type requires that the error implements the `Error` protocol. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] pub(crate) struct BuiltInResult { - pub ok_ty: Box, - pub err_ty: Box, + pub ok_ty: Box, + pub err_ty: Box, } impl BuiltInResult { @@ -40,28 +40,19 @@ impl BuiltInResult { swift_bridge_path: &Path, types: &TypeDeclarations, ) -> TokenStream { - let ok_ffi_repr = self - .ok_ty - .to_ffi_compatible_rust_type(swift_bridge_path, types); - let err_ffi_repr = self - .err_ty - .to_ffi_compatible_rust_type(swift_bridge_path, types); - - let convert_ok = self.ok_ty.convert_ffi_value_to_rust_value( - "e! { #expression.ok_or_err as #ok_ffi_repr }, - span, + let convert_ok = self.ok_ty.convert_ffi_result_ok_value_to_rust_value( + expression, swift_bridge_path, types, ); - let convert_err = self.err_ty.convert_ffi_value_to_rust_value( - "e! { #expression.ok_or_err as #err_ffi_repr }, - span, + let convert_err = self.err_ty.convert_ffi_result_err_value_to_rust_value( + expression, swift_bridge_path, types, ); - quote! { + quote_spanned! {span=> if #expression.is_ok { std::result::Result::Ok(#convert_ok) } else { @@ -92,10 +83,10 @@ impl BuiltInResult { ) -> String { let convert_ok = self .ok_ty - .convert_swift_expression_to_ffi_compatible("ok", type_pos); + .convert_swift_expression_to_ffi_type("ok", type_pos); let convert_err = self .err_ty - .convert_swift_expression_to_ffi_compatible("err", type_pos); + .convert_swift_expression_to_ffi_type("err", type_pos); format!( "{{ switch {val} {{ case .Ok(let ok): return __private__ResultPtrAndPtr(is_ok: true, ok_or_err: {convert_ok}) case .Err(let err): return __private__ResultPtrAndPtr(is_ok: false, ok_or_err: {convert_err}) }} }}()", diff --git a/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs b/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs new file mode 100644 index 00000000..ee0745d1 --- /dev/null +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_str.rs @@ -0,0 +1,2 @@ +#[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 new file mode 100644 index 00000000..d052f6c7 --- /dev/null +++ b/crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs @@ -0,0 +1,257 @@ +use crate::bridged_type::{BridgeableType, TypePosition, UnusedOptionNoneValue}; +use crate::TypeDeclarations; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{Path, Type}; + +#[derive(Debug)] +pub(crate) struct BridgedString; + +impl BridgeableType for BridgedString { + fn is_built_in_type(&self) -> bool { + true + } + + fn to_rust_type_path(&self) -> TokenStream { + // FIXME: Change to `::std::string::String` + quote! { String } + } + + fn to_swift_type(&self, type_pos: TypePosition, _types: &TypeDeclarations) -> String { + match type_pos { + TypePosition::FnArg(_func_host_lang, _) => "GenericIntoRustString".to_string(), + TypePosition::FnReturn(_func_host_lang) => "RustString".to_string(), + TypePosition::SharedStructField => "RustString".to_string(), + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + "UnsafeMutableRawPointer?".to_string() + } + } + } + + fn to_c_type(&self) -> String { + "void*".to_string() + } + + fn to_c_include(&self) -> Option<&'static str> { + None + } + + fn to_ffi_compatible_rust_type( + &self, + swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + quote! { *mut #swift_bridge_path::string::RustString } + } + + fn to_ffi_compatible_option_rust_type( + &self, + swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + self.to_ffi_compatible_rust_type(swift_bridge_path, types) + } + + fn to_ffi_compatible_option_swift_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> String { + todo!() + } + + fn to_ffi_compatible_option_c_type(&self) -> String { + "void*".to_string() + } + + fn convert_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + quote! { + #swift_bridge_path::string::RustString( #expression ).box_into_raw() + } + } + + fn convert_option_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + swift_bridge_path: &Path, + ) -> TokenStream { + let unused_none_value = BridgedString.unused_option_none_val(swift_bridge_path).rust; + + quote! { + if let Some(val) = #expression { + #swift_bridge_path::string::RustString(val).box_into_raw() + } else { + #unused_none_value + } + } + } + + fn convert_swift_expression_to_ffi_type( + &self, + expression: &str, + _type_pos: TypePosition, + ) -> String { + format!( + "{{ let rustString = {value}.intoRustString(); rustString.isOwned = false; return rustString.ptr }}()", + value = expression + ) + } + + fn convert_option_swift_expression_to_ffi_type( + &self, + expression: &str, + type_pos: TypePosition, + ) -> String { + match type_pos { + TypePosition::FnArg(_func_host_lang, _) => { + format!( + "{{ if let rustString = optionalStringIntoRustString({expression}) {{ rustString.isOwned = false; return rustString.ptr }} else {{ return nil }} }}()", + expression = expression + ) + } + TypePosition::FnReturn(_) => { + todo!("Need to come back and think through what should happen here...") + } + TypePosition::SharedStructField => { + todo!("Option fields in structs are not yet supported.") + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + unimplemented!() + } + } + } + + fn convert_ffi_expression_to_rust_type( + &self, + expression: &TokenStream, + span: Span, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + quote_spanned! {span=> + unsafe { Box::from_raw(#expression).0 } + } + } + + 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 } ) + } + } + } + + fn convert_ffi_expression_to_swift_type( + &self, + expression: &str, + type_pos: TypePosition, + _types: &TypeDeclarations, + ) -> String { + match type_pos { + TypePosition::FnArg(_, _) + | TypePosition::FnReturn(_) + | TypePosition::SharedStructField => { + format!("RustString(ptr: {})", expression) + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + format!("RustString(ptr: {}!)", expression) + } + } + } + + fn convert_ffi_option_expression_to_swift_type(&self, expression: &str) -> String { + format!("{{ let val = {expression}; if val != nil {{ return RustString(ptr: val!) }} else {{ return nil }} }}()", expression = expression,) + } + + fn convert_ffi_result_ok_value_to_rust_value( + &self, + result: &TokenStream, + swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + quote! { + unsafe { + Box::from_raw(#result.ok_or_err as *mut #swift_bridge_path::string::RustString).0 + } + } + } + + fn convert_ffi_result_err_value_to_rust_value( + &self, + result: &TokenStream, + swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + quote! { + unsafe { + Box::from_raw(#result.ok_or_err as *mut #swift_bridge_path::string::RustString).0 + } + } + } + + fn unused_option_none_val(&self, swift_bridge_path: &Path) -> UnusedOptionNoneValue { + UnusedOptionNoneValue { + rust: quote! { + std::ptr::null::<#swift_bridge_path::string::RustString>() as *mut #swift_bridge_path::string::RustString + }, + // TODO: Add integration tests: + // Rust: crates/swift-integration-tests/src/option.rs + // Swift: OptionTests.swift + swift: "TODO_SWIFT_OPTIONAL_STRING_SUPPORT".to_string(), + } + } + + fn can_parse_token_stream_str(tokens: &str) -> bool + where + Self: Sized, + { + tokens == "String" + } + + fn from_type(ty: &Type, types: &TypeDeclarations) -> Option + where + Self: Sized, + { + match ty { + Type::Path(path) => Self::parse_token_stream_str( + path.path.segments.to_token_stream().to_string().as_str(), + types, + ), + _ => None, + } + } + + fn parse_token_stream_str(_tokens: &str, _types: &TypeDeclarations) -> Option + where + Self: Sized, + { + Some(BridgedString) + } + + fn is_null(&self) -> bool { + false + } + + fn is_str(&self) -> bool { + false + } + + fn contains_owned_string_recursive(&self) -> bool { + true + } + + fn contains_ref_string_recursive(&self) -> bool { + false + } + + fn has_swift_bridge_copy_annotation(&self) -> bool { + false + } +} 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 new file mode 100644 index 00000000..221f808e --- /dev/null +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs @@ -0,0 +1,659 @@ +use crate::bridged_type::{BridgeableType, TypePosition, UnusedOptionNoneValue}; +use crate::parse::{HostLang, OpaqueRustTypeGenerics}; +use crate::{TypeDeclarations, SWIFT_BRIDGE_PREFIX}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, quote_spanned, ToTokens}; +use std::fmt::{Debug, Formatter}; +use std::ops::Deref; +use syn::{Path, Type}; + +#[derive(Clone)] +pub(crate) struct OpaqueForeignType { + pub ty: Ident, + pub host_lang: HostLang, + pub reference: bool, + pub mutable: bool, + pub has_swift_bridge_copy_annotation: bool, + pub generics: OpaqueRustTypeGenerics, +} + +impl BridgeableType for OpaqueForeignType { + fn is_built_in_type(&self) -> bool { + false + } + + fn to_rust_type_path(&self) -> TokenStream { + let ty_name = &self.ty; + + if self.host_lang.is_rust() { + quote! { + super:: #ty_name + } + } else { + quote! { + #ty_name + } + } + } + + fn to_swift_type(&self, type_pos: TypePosition, types: &TypeDeclarations) -> String { + if self.host_lang.is_rust() { + match type_pos { + TypePosition::FnArg(func_host_lang, _) | TypePosition::FnReturn(func_host_lang) => { + if func_host_lang.is_rust() { + let mut class_name = self.ty.to_string(); + + if !self.has_swift_bridge_copy_annotation { + if self.reference { + class_name += "Ref"; + } + + if self.mutable { + class_name += "Mut"; + } + } + + format!( + "{}{}", + class_name, + self.generics + .angle_bracketed_generic_concrete_swift_types_string(types) + ) + } else { + format!("UnsafeMutableRawPointer") + } + } + TypePosition::SharedStructField => { + // + unimplemented!() + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + unimplemented!() + } + } + } else { + match type_pos { + TypePosition::FnArg(func_host_lang, _) | TypePosition::FnReturn(func_host_lang) => { + if func_host_lang.is_rust() { + self.ty.to_string() + } else { + "UnsafeMutableRawPointer".to_string() + } + } + TypePosition::SharedStructField => { + // + unimplemented!() + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + unimplemented!() + } + } + } + } + + fn to_c_type(&self) -> String { + if self.host_lang.is_rust() { + if self.has_swift_bridge_copy_annotation { + format!("struct {}", self.copy_ffi_repr_type_string()) + } else { + "void*".to_string() + } + } else { + "void*".to_string() + } + } + + fn to_c_include(&self) -> Option<&'static str> { + None + } + + fn to_ffi_compatible_rust_type( + &self, + _swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + let ty_name = &self.ty; + + if self.has_swift_bridge_copy_annotation { + let ty = self.copy_rust_repr_type(); + quote! { #ty } + } else { + if self.host_lang.is_rust() { + let generics = self + .generics + .angle_bracketed_concrete_generics_tokens(types); + + if self.reference { + let ptr = if self.mutable { + quote! { *mut } + } else { + quote! { *const } + }; + + quote_spanned! {ty_name.span()=> #ptr super::#ty_name } + } else { + quote! { *mut super::#ty_name #generics } + } + } else { + quote! { #ty_name } + } + } + } + + fn to_ffi_compatible_option_rust_type( + &self, + _swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + let type_name = &self.ty; + + if self.has_swift_bridge_copy_annotation { + let option_ty = self.option_copy_rust_repr_type(); + quote! { #option_ty } + } else { + let generics = self + .generics + .angle_bracketed_concrete_generics_tokens(types); + quote! { *mut super::#type_name #generics } + } + } + + fn to_ffi_compatible_option_swift_type( + &self, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> String { + todo!() + } + + fn to_ffi_compatible_option_c_type(&self) -> String { + if self.has_swift_bridge_copy_annotation { + self.option_copy_ffi_repr_type_string() + } else { + "void*".to_string() + } + } + + fn convert_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + _swift_bridge_path: &Path, + types: &TypeDeclarations, + ) -> TokenStream { + let ty_name = &self.ty; + + if self.host_lang.is_rust() { + if self.has_swift_bridge_copy_annotation { + let copy_ty = self.copy_rust_repr_type(); + quote! { + #copy_ty::from_rust_repr(#expression) + } + } else if self.reference { + let ptr = if self.mutable { + quote! { *mut } + } else { + quote! { *const } + }; + + quote! { + #expression as #ptr super::#ty_name + } + } else { + let generics = self + .generics + .angle_bracketed_concrete_generics_tokens(types); + quote! { + Box::into_raw(Box::new(#expression)) as *mut super::#ty_name #generics + } + } + } else { + quote! { + #expression + } + } + } + + fn convert_option_rust_expression_to_ffi_type( + &self, + expression: &TokenStream, + _swift_bridge_path: &Path, + ) -> TokenStream { + if self.has_swift_bridge_copy_annotation { + let copy_repr = self.copy_rust_repr_type(); + let option_copy_repr = self.option_copy_rust_repr_type(); + + quote! { + if let Some(val) = #expression { + #option_copy_repr { + is_some: true, + val: std::mem::MaybeUninit::new(#copy_repr::from_rust_repr(val)) + } + } else { + #option_copy_repr { + is_some: false, + val: std::mem::MaybeUninit::uninit() + } + } + } + } else { + quote! { + if let Some(val) = #expression { + Box::into_raw(Box::new(val)) + } else { + std::ptr::null_mut() + } + } + } + } + + fn convert_swift_expression_to_ffi_type( + &self, + expression: &str, + type_pos: TypePosition, + ) -> String { + let ty_name = &self.ty; + + if self.host_lang.is_rust() { + if self.has_swift_bridge_copy_annotation { + format!("{}.intoFfiRepr()", expression) + } else if self.reference { + format!("{}.ptr", expression) + } else { + match type_pos { + TypePosition::FnArg(func_host_lang, _) + | TypePosition::FnReturn(func_host_lang) => { + if func_host_lang.is_rust() { + format!( + "{{{}.isOwned = false; return {}.ptr;}}()", + expression, expression + ) + } else { + todo!() + } + } + TypePosition::SharedStructField => { + todo!("Opaque types in shared struct fields are not yet supported") + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + unimplemented!() + } + } + } + } else { + match type_pos { + TypePosition::FnArg(func_host_lang, _) => { + if func_host_lang.is_rust() { + format!("Unmanaged.passRetained({}).toOpaque()", expression) + } else { + format!( + "Unmanaged<{type_name}>.fromOpaque({value}).takeRetainedValue()", + type_name = ty_name, + value = expression + ) + } + } + TypePosition::FnReturn(_func_host_lang) => { + format!("Unmanaged.passRetained({}).toOpaque()", expression) + } + TypePosition::SharedStructField => { + todo!("Opaque types in shared struct fields are not yet supported") + } + TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { + unimplemented!() + } + } + } + } + + fn convert_option_swift_expression_to_ffi_type( + &self, + expression: &str, + _type_pos: TypePosition, + ) -> String { + if self.has_swift_bridge_copy_annotation { + let ffi_repr = self.copy_ffi_repr_type_string(); + let option_ffi_repr = self.option_copy_ffi_repr_type_string(); + + format!("{option_ffi_repr}(is_some: {expression} != nil, val: {{ if let val = {expression} {{ return val.intoFfiRepr() }} else {{ return {ffi_repr}() }} }}() )", expression = expression, + option_ffi_repr = option_ffi_repr, + ffi_repr = ffi_repr + ) + } else { + format!("{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()", expression = expression,) + } + } + + fn convert_ffi_expression_to_rust_type( + &self, + expression: &TokenStream, + _span: Span, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + if self.host_lang.is_rust() { + if self.has_swift_bridge_copy_annotation { + let maybe_ref = if self.reference { + quote! {&} + } else { + quote! {} + }; + quote! { + #maybe_ref #expression.into_rust_repr() + } + } else if self.reference { + let maybe_mut = if self.mutable { + quote! { mut } + } else { + quote! {} + }; + + quote! { + unsafe { & #maybe_mut * #expression } + } + } else { + quote! { + unsafe { * Box::from_raw( #expression ) } + } + } + } else { + if self.reference { + todo!("Handle referenced self Swift types") + } else { + quote! { + #expression + } + } + } + } + + fn convert_ffi_option_expression_to_rust_type(&self, expression: &TokenStream) -> TokenStream { + if self.has_swift_bridge_copy_annotation { + quote! { + if #expression.is_some { + Some(unsafe{ #expression.val.assume_init() }.into_rust_repr()) + } else { + None + } + } + } else { + quote! { + if #expression.is_null() { + None + } else { + Some(unsafe { * Box::from_raw(#expression) } ) + } + } + } + } + + fn convert_ffi_expression_to_swift_type( + &self, + expression: &str, + type_pos: TypePosition, + _types: &TypeDeclarations, + ) -> String { + let mut ty_name = self.ty.to_string(); + + if self.reference { + ty_name += "Ref"; + } + if self.mutable { + ty_name += "Mut"; + } + + if self.host_lang.is_rust() { + if self.has_swift_bridge_copy_annotation { + format!( + "{ty_name}(bytes: {value})", + ty_name = ty_name, + value = expression, + ) + } else { + match type_pos { + TypePosition::FnReturn(fn_host_lang) if fn_host_lang.is_swift() => { + format!("Unmanaged.passRetained({expression}).toOpaque()") + } + _ => { + format!( + "{ty_name}(ptr: {value})", + ty_name = ty_name, + value = expression, + ) + } + } + } + } else { + format!( + "Unmanaged<{ty_name}>.fromOpaque({value}).takeRetainedValue()", + ty_name = ty_name, + value = expression + ) + } + } + + fn convert_ffi_option_expression_to_swift_type(&self, expression: &str) -> String { + if self.has_swift_bridge_copy_annotation { + let type_name = self.swift_name(); + format!( + "{{ let val = {expression}; if val.is_some {{ return {type_name}(bytes: val.val) }} else {{ return nil }} }}()", + expression = expression, + type_name = type_name + ) + } else { + let type_name = self.swift_name(); + format!( + "{{ let val = {expression}; if val != nil {{ return {type_name}(ptr: val!) }} else {{ return nil }} }}()", + expression = expression, + type_name = type_name + ) + } + } + + fn convert_ffi_result_ok_value_to_rust_value( + &self, + result: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + let ty = &self.ty; + + match self.host_lang { + HostLang::Rust => { + quote! { + unsafe { *Box::from_raw(#result.ok_or_err as *mut super::#ty) } + } + } + HostLang::Swift => { + quote! { + unsafe { #ty(#result.ok_or_err) } + } + } + } + } + + fn convert_ffi_result_err_value_to_rust_value( + &self, + result: &TokenStream, + _swift_bridge_path: &Path, + _types: &TypeDeclarations, + ) -> TokenStream { + let ty = &self.ty; + + match self.host_lang { + HostLang::Rust => { + quote! { + unsafe { *Box::from_raw(#result.ok_or_err as *mut super::#ty) } + } + } + HostLang::Swift => { + quote! { + unsafe { #ty(#result.ok_or_err) } + } + } + } + } + + fn unused_option_none_val(&self, _swift_bridge_path: &Path) -> UnusedOptionNoneValue { + let ty_name = &self.ty; + + if self.reference { + todo!("Support returning Option<&T> where T is an opaque type") + } else { + UnusedOptionNoneValue { + rust: quote! { std::ptr::null::<#ty_name>() as *mut super::#ty_name }, + swift: "TODO..Support Swift Option::None value".into(), + } + } + } + + fn can_parse_token_stream_str(_tokens: &str) -> bool + where + Self: Sized, + { + // TODO: Make this unreachable!() once we finish moving towards `BridgeableType` instead + // of the old `BridgedType`. + // unreachable!() + true + } + + fn from_type(ty: &Type, types: &TypeDeclarations) -> Option + where + Self: Sized, + { + match ty { + Type::Path(path) => { + if let Some(ty) = types.get_with_type_path(path) { + ty.to_opaque_type(false, false) + } else { + Self::parse_token_stream_str( + path.path.segments.to_token_stream().to_string().as_str(), + types, + ) + } + } + Type::Reference(ty_ref) => match ty_ref.elem.deref() { + Type::Path(p) => { + if let Some(ty) = types.get_with_type_path(p) { + ty.to_opaque_type(true, ty_ref.mutability.is_some()) + } else { + None + } + } + _ => None, + }, + _ => None, + } + } + + fn parse_token_stream_str(tokens: &str, types: &TypeDeclarations) -> Option + where + Self: Sized, + { + let bridged_type = types.get(tokens)?; + bridged_type.to_opaque_type(false, false) + } + + fn is_null(&self) -> bool { + false + } + + fn is_str(&self) -> bool { + false + } + + fn contains_owned_string_recursive(&self) -> bool { + false + } + + fn contains_ref_string_recursive(&self) -> bool { + false + } + + fn has_swift_bridge_copy_annotation(&self) -> bool { + self.has_swift_bridge_copy_annotation + } +} + +impl OpaqueForeignType { + pub fn swift_name(&self) -> String { + format!("{}", self.ty) + } + + /// The name of the type used to pass a `#[swift_bridge(Copy(...))]` type over FFI + /// + /// __swift_bridge__SomeType + pub fn copy_rust_repr_type(&self) -> Ident { + let ty = format!( + "{}{}{}", + SWIFT_BRIDGE_PREFIX, + self.ty, + self.generics.underscore_prefixed_generics_string() + ); + Ident::new(&ty, self.ty.span()) + } + + /// The name of the type used to pass a `Option` where T is + /// `#[swift_bridge(Copy(...))]` over FFI + /// + /// __swift_bridge__Option_SomeType + pub fn option_copy_rust_repr_type(&self) -> Ident { + let ty = format!( + "{}Option_{}{}", + SWIFT_BRIDGE_PREFIX, + self.ty, + self.generics.underscore_prefixed_generics_string() + ); + Ident::new(&ty, self.ty.span()) + } + + /// The FFI name of the type used to pass a `Option` where T is + /// `#[swift_bridge(Copy(...))]` over FFI + /// + /// __swift_bridge__$Option$SomeType + pub fn option_copy_ffi_repr_type_string(&self) -> String { + format!( + "{}$Option${}{}", + SWIFT_BRIDGE_PREFIX, + self.ty, + self.generics.dollar_prefixed_generics_string() + ) + } + + /// The name of the type used to pass a `#[swift_bridge(Copy(...))]` type over FFI + pub fn copy_ffi_repr_type_string(&self) -> String { + format!( + "{}${}{}", + SWIFT_BRIDGE_PREFIX, + self.ty, + self.generics.dollar_prefixed_generics_string() + ) + } +} + +impl Debug for OpaqueForeignType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpaqueForeignType") + .field("ty", &self.ty.to_token_stream()) + .field("host_lang", &self.host_lang) + .field("reference", &self.reference) + .field("mutable", &self.mutable) + .finish() + } +} + +impl PartialEq for OpaqueForeignType { + fn eq(&self, other: &Self) -> bool { + self.ty.to_token_stream().to_string() == other.ty.to_token_stream().to_string() + && self.host_lang == other.host_lang + && self.reference == other.reference + && self.mutable == other.mutable + } +} + +impl Deref for OpaqueForeignType { + type Target = Ident; + + fn deref(&self) -> &Self::Target { + &self.ty + } +} 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 e5e53f75..29c252c0 100644 --- a/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs +++ b/crates/swift-bridge-ir/src/bridged_type/bridged_option.rs @@ -5,19 +5,17 @@ use std::ops::Deref; use syn::Path; /// Option -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] pub(crate) struct BridgedOption { pub ty: Box, } impl BridgedOption { - pub(super) fn convert_rust_value_to_ffi_value( + pub(super) fn convert_rust_expression_to_ffi_type( &self, expression: &TokenStream, swift_bridge_path: &Path, ) -> TokenStream { - let unused_none_value = self.ty.rust_unused_option_none_val(swift_bridge_path).rust; - let option_rust_primitive_to_ffi_primitive = move |ffi_option_name: TokenStream, unused_none: TokenStream| { quote! { @@ -30,6 +28,9 @@ impl BridgedOption { }; match self.ty.deref() { + BridgedType::Bridgeable(b) => { + b.convert_option_rust_expression_to_ffi_type(expression, swift_bridge_path) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -88,15 +89,6 @@ impl BridgedOption { } } } - StdLibType::String => { - quote! { - if let Some(val) = #expression { - #swift_bridge_path::string::RustString(val).box_into_raw() - } else { - #unused_none_value - } - } - } StdLibType::Vec(_) => { todo!("Support Option>") } @@ -122,39 +114,15 @@ impl BridgedOption { #option_name::from_rust_repr(#expression) } } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.has_swift_bridge_copy_annotation { - let copy_repr = opaque.copy_rust_repr_type(); - let option_copy_repr = opaque.option_copy_rust_repr_type(); - - quote! { - if let Some(val) = #expression { - #option_copy_repr { - is_some: true, - val: std::mem::MaybeUninit::new(#copy_repr::from_rust_repr(val)) - } - } else { - #option_copy_repr { - is_some: false, - val: std::mem::MaybeUninit::uninit() - } - } - } - } else { - quote! { - if let Some(val) = #expression { - Box::into_raw(Box::new(val)) - } else { - std::ptr::null_mut() - } - } - } - } } } - pub(super) fn convert_ffi_value_to_rust_value(&self, value: &TokenStream) -> TokenStream { + pub(super) fn convert_ffi_expression_to_rust_type( + &self, + expression: &TokenStream, + ) -> TokenStream { match self.ty.deref() { + BridgedType::Bridgeable(b) => b.convert_ffi_option_expression_to_rust_type(expression), BridgedType::StdLib(stdlib_ty) => match stdlib_ty { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -172,7 +140,7 @@ impl BridgedOption { | StdLibType::F32 | StdLibType::F64 | StdLibType::Bool => { - quote! { if #value.is_some { Some(#value.val) } else { None } } + quote! { if #expression.is_some { Some(#expression.val) } else { None } } } StdLibType::Pointer(_) => { todo!("Option<*const T> and Option<*mut T> are not yet supported.") @@ -182,16 +150,7 @@ impl BridgedOption { } StdLibType::Str => { quote! { - if #value.start.is_null() { None } else { Some(#value.to_str()) } - } - } - StdLibType::String => { - quote! { - if #value.is_null() { - None - } else { - Some(unsafe { Box::from_raw(#value).0 } ) - } + if #expression.start.is_null() { None } else { Some(#expression.to_str()) } } } StdLibType::Vec(_) => { @@ -209,38 +168,20 @@ impl BridgedOption { }, BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(_shared_struct))) => { quote! { - #value.into_rust_repr() + #expression.into_rust_repr() } } BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(_shared_enum))) => { quote! { - #value.into_rust_repr() - } - } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.has_swift_bridge_copy_annotation { - quote! { - if #value.is_some { - Some(unsafe{ #value.val.assume_init() }.into_rust_repr()) - } else { - None - } - } - } else { - quote! { - if #value.is_null() { - None - } else { - Some(unsafe { * Box::from_raw(#value) } ) - } - } + #expression.into_rust_repr() } } } } - pub(super) fn convert_ffi_expression_to_swift(&self, expression: &str) -> String { + pub(super) fn convert_ffi_expression_to_swift_type(&self, expression: &str) -> String { match self.ty.deref() { + BridgedType::Bridgeable(b) => b.convert_ffi_option_expression_to_swift_type(expression), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -272,9 +213,6 @@ impl BridgedOption { val = expression, ) } - StdLibType::String => { - format!("{{ let val = {expression}; if val != nil {{ return RustString(ptr: val!) }} else {{ return nil }} }}()", expression = expression,) - } StdLibType::Vec(_) => { todo!("Support Option>") } @@ -294,27 +232,10 @@ impl BridgedOption { BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(_shared_enum))) => { format!("{expression}.intoSwiftRepr()", expression = expression) } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.has_swift_bridge_copy_annotation { - let type_name = opaque.swift_name(); - format!( - "{{ let val = {expression}; if val.is_some {{ return {type_name}(bytes: val.val) }} else {{ return nil }} }}()", - expression = expression, - type_name = type_name - ) - } else { - let type_name = opaque.swift_name(); - format!( - "{{ let val = {expression}; if val != nil {{ return {type_name}(ptr: val!) }} else {{ return nil }} }}()", - expression = expression, - type_name = type_name - ) - } - } } } - pub fn convert_swift_expression_to_ffi_compatible( + pub fn convert_swift_expression_to_ffi_type( &self, expression: &str, type_pos: TypePosition, @@ -329,6 +250,9 @@ impl BridgedOption { }; match self.ty.deref() { + BridgedType::Bridgeable(b) => { + b.convert_option_swift_expression_to_ffi_type(expression, type_pos) + } BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -355,23 +279,6 @@ impl BridgedOption { StdLibType::Str => { format!("{expression}AsRustStr", expression = expression) } - StdLibType::String => match type_pos { - TypePosition::FnArg(_func_host_lang, _) => { - format!( - "{{ if let rustString = optionalStringIntoRustString({expression}) {{ rustString.isOwned = false; return rustString.ptr }} else {{ return nil }} }}()", - expression = expression - ) - } - TypePosition::FnReturn(_) => { - todo!("Need to come back and think through what should happen here...") - } - TypePosition::SharedStructField => { - todo!("Option fields in structs are not yet supported.") - } - TypePosition::SwiftCallsRustAsyncOnCompleteReturnTy => { - unimplemented!() - } - }, StdLibType::Vec(_) => { todo!("Option is not yet supported") } @@ -401,19 +308,6 @@ impl BridgedOption { expression = expression ) } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.has_swift_bridge_copy_annotation { - let ffi_repr = opaque.copy_ffi_repr_type_string(); - let option_ffi_repr = opaque.option_copy_ffi_repr_type_string(); - - format!("{option_ffi_repr}(is_some: {expression} != nil, val: {{ if let val = {expression} {{ return val.intoFfiRepr() }} else {{ return {ffi_repr}() }} }}() )", expression = expression, - option_ffi_repr = option_ffi_repr, - ffi_repr = ffi_repr - ) - } else { - format!("{{ if let val = {expression} {{ val.isOwned = false; return val.ptr }} else {{ return nil }} }}()", expression = expression,) - } - } } } } @@ -421,6 +315,7 @@ impl BridgedOption { impl BridgedOption { pub fn to_c(&self) -> String { match self.ty.deref() { + BridgedType::Bridgeable(b) => b.to_ffi_compatible_option_c_type(), BridgedType::StdLib(stdlib_type) => match stdlib_type { StdLibType::Null => { todo!("Option<()> is not yet supported") @@ -445,7 +340,6 @@ impl BridgedOption { todo!("Option<&[T]> is not yet supported") } StdLibType::Str => "struct RustStr".to_string(), - StdLibType::String => "void*".to_string(), StdLibType::Vec(_) => { todo!("Option> is not yet supported") } @@ -465,13 +359,6 @@ impl BridgedOption { BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(shared_enum))) => { format!("struct {}", shared_enum.ffi_option_name_string()) } - BridgedType::Foreign(CustomBridgedType::Opaque(opaque)) => { - if opaque.has_swift_bridge_copy_annotation { - opaque.option_copy_ffi_repr_type_string() - } else { - "void*".to_string() - } - } } } } @@ -488,11 +375,14 @@ mod tests { #[test] fn parse_option_static_str() { let type_str = "Option < & 'static str >"; - assert_eq!( - BridgedType::new_with_str(type_str, &TypeDeclarations::default()).unwrap(), - BridgedType::StdLib(StdLibType::Option(BridgedOption { - ty: Box::new(BridgedType::StdLib(StdLibType::Str)) - })) - ); + + let parsed = BridgedType::new_with_str(type_str, &TypeDeclarations::default()).unwrap(); + match parsed { + BridgedType::StdLib(StdLibType::Option(opt)) => match *opt.ty { + BridgedType::StdLib(StdLibType::Str) => {} + _ => panic!(), + }, + _ => panic!(), + } } } 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 f046be02..1b6bb9df 100644 --- a/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs +++ b/crates/swift-bridge-ir/src/bridged_type/shared_struct.rs @@ -84,7 +84,7 @@ impl SharedStruct { let access_field = norm_field.append_field_accessor("e! {val}); let ty = BridgedType::new_with_type(&norm_field.ty, types).unwrap(); - let converted_field = ty.convert_ffi_value_to_rust_value( + let converted_field = ty.convert_ffi_expression_to_rust_type( &access_field, norm_field.ty.span(), swift_bridge_path, @@ -125,11 +125,8 @@ impl SharedStruct { let access_field = norm_field.append_field_accessor("e! {val}); let ty = BridgedType::new_with_type(&norm_field.ty, types).unwrap(); - let converted_field = ty.convert_rust_value_to_ffi_compatible_value( - &access_field, - swift_bridge_path, - types, - ); + let converted_field = + ty.convert_rust_expression_to_ffi_type(&access_field, swift_bridge_path, types); quote! { #maybe_name_and_colon #converted_field @@ -166,7 +163,7 @@ impl SharedStruct { .map(|norm_field| { let field_name = norm_field.ffi_field_name(); let ty = BridgedType::new_with_type(&norm_field.ty, types).unwrap(); - let access_field = ty.convert_swift_expression_to_ffi_compatible( + let access_field = ty.convert_swift_expression_to_ffi_type( &format!("val.{field_name}", field_name = field_name), TypePosition::SharedStructField, ); diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs index ff0cf1a6..0a766ef0 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/extern_swift_function_opaque_swift_type_return_codegen_tests.rs @@ -37,8 +37,8 @@ mod test_extern_swift_freestanding_function_owned_opaque_swift_type_return { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function () -> __private__PointerToSwiftType { - __private__PointerToSwiftType(ptr: Unmanaged.passRetained(some_function()).toOpaque()) +func __swift_bridge__some_function () -> UnsafeMutableRawPointer { + Unmanaged.passRetained(some_function()).toOpaque() } "#, ) @@ -97,8 +97,8 @@ mod test_extern_swift_method_owned_opaque_swift_type_return { ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$SomeType$some_method") -func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> __private__PointerToSwiftType { - __private__PointerToSwiftType(ptr: Unmanaged.passRetained(Unmanaged.fromOpaque(this).takeUnretainedValue().some_method()).toOpaque()) +func __swift_bridge__SomeType_some_method (_ this: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + Unmanaged.passRetained(Unmanaged.fromOpaque(this).takeUnretainedValue().some_method()).toOpaque() } "#, ) diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs index 4156428d..3ef8d5d7 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/opaque_swift_type_codegen_tests.rs @@ -45,8 +45,8 @@ mod extern_swift_freestanding_fn_with_owned_opaque_swift_type_arg { const EXPECTED_SWIFT_CODE: ExpectedSwiftCode = ExpectedSwiftCode::ContainsAfterTrim( r#" @_cdecl("__swift_bridge__$some_function") -func __swift_bridge__some_function (_ arg: __private__PointerToSwiftType) { - some_function(arg: Unmanaged.fromOpaque(arg.ptr).takeRetainedValue()) +func __swift_bridge__some_function (_ arg: UnsafeMutableRawPointer) { + some_function(arg: Unmanaged.fromOpaque(arg).takeRetainedValue()) } "#, ); diff --git a/crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs b/crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs index 6e2fc22f..7f6db2fd 100644 --- a/crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs +++ b/crates/swift-bridge-ir/src/codegen/codegen_tests/result_codegen_tests.rs @@ -125,3 +125,68 @@ void __swift_bridge__$some_function(struct __private__ResultPtrAndPtr arg); .test(); } } + +/// Test code generation for Rust function that accepts and returns a Result +/// where T and E are opaque Swift types. +mod extern_rust_fn_result_opaque_swift { + use super::*; + + fn bridge_module_tokens() -> TokenStream { + quote! { + mod ffi { + extern "Swift" { + type SomeType; + } + + extern "Rust" { + fn some_function (arg: Result); + } + } + } + } + + fn expected_rust_tokens() -> ExpectedRustTokens { + ExpectedRustTokens::Contains(quote! { + + #[export_name = "__swift_bridge__$some_function"] + pub extern "C" fn __swift_bridge__some_function( + arg: swift_bridge::result::ResultPtrAndPtr + ) { + super::some_function( + if arg.is_ok { + std::result::Result::Ok(unsafe { SomeType(arg.ok_or_err) }) + } else { + std::result::Result::Err(unsafe { SomeType(arg.ok_or_err) }) + } + ) + } + }) + } + + fn expected_swift_code() -> ExpectedSwiftCode { + ExpectedSwiftCode::ContainsAfterTrim( + r#" +func some_function(_ arg: RustResult) { + __swift_bridge__$some_function({ switch arg { case .Ok(let ok): return __private__ResultPtrAndPtr(is_ok: true, ok_or_err: Unmanaged.passRetained(ok).toOpaque()) case .Err(let err): return __private__ResultPtrAndPtr(is_ok: false, ok_or_err: Unmanaged.passRetained(err).toOpaque()) } }()) +} +"#, + ) + } + + const EXPECTED_C_HEADER: ExpectedCHeader = ExpectedCHeader::ContainsAfterTrim( + r#" +void __swift_bridge__$some_function(struct __private__ResultPtrAndPtr arg); + "#, + ); + + #[test] + fn extern_rust_fn_result_opaque_swift() { + CodegenTest { + bridge_module: bridge_module_tokens().into(), + expected_rust_tokens: expected_rust_tokens(), + expected_swift_code: expected_swift_code(), + expected_c_header: EXPECTED_C_HEADER, + } + .test(); + } +} diff --git a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs index dc091e50..a5b4274d 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_c_header.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_c_header.rs @@ -71,7 +71,7 @@ impl SwiftBridgeModule { for field in f.iter() { let ty = BridgedType::new_with_type(&field.ty, &self.types) .unwrap(); - if let Some(include) = ty.c_include() { + if let Some(include) = ty.to_c_include() { bookkeeping.includes.insert(include); } @@ -84,7 +84,7 @@ impl SwiftBridgeModule { for (idx, field) in types.iter().enumerate() { let ty = BridgedType::new_with_type(&field.ty, &self.types) .unwrap(); - if let Some(include) = ty.c_include() { + if let Some(include) = ty.to_c_include() { bookkeeping.includes.insert(include); } @@ -302,7 +302,7 @@ fn declare_func( let declaration = if func.sig.asyncness.is_some() { let maybe_ret = BridgedType::new_with_return_type(&func.sig.output, types).unwrap(); - let maybe_ret = if maybe_ret == BridgedType::StdLib(StdLibType::Null) { + let maybe_ret = if maybe_ret.is_null() { "".to_string() } else { format!(", {} ret", maybe_ret.to_c()) @@ -666,7 +666,7 @@ struct __private__FfiSlice __swift_bridge__$bar(void); } }; let expected = r#" -struct __private__PointerToSwiftType __swift_bridge__$some_function(void); +void* __swift_bridge__$some_function(void); "#; let module = parse_ok(tokens); diff --git a/crates/swift-bridge-ir/src/codegen/generate_swift.rs b/crates/swift-bridge-ir/src/codegen/generate_swift.rs index 484be7dd..df638cff 100644 --- a/crates/swift-bridge-ir/src/codegen/generate_swift.rs +++ b/crates/swift-bridge-ir/src/codegen/generate_swift.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use syn::Path; -use crate::bridged_type::{BridgedType, TypePosition}; +use crate::bridged_type::{BridgeableType, BridgedType, TypePosition}; use crate::codegen::generate_swift::generate_function_swift_calls_rust::gen_func_swift_calls_rust; use crate::codegen::generate_swift::opaque_copy_type::generate_opaque_copy_struct; use crate::codegen::generate_swift::swift_class::generate_swift_class; @@ -223,20 +223,17 @@ fn gen_function_exposes_swift_to_rust( call_fn = call_fn ); - call_fn = built_in.convert_swift_expression_to_ffi_compatible( + call_fn = built_in.convert_swift_expression_to_ffi_type( &call_fn, TypePosition::FnReturn(func.host_lang), ); } else if func.is_swift_initializer { - call_fn = format!( - "__private__PointerToSwiftType(ptr: Unmanaged.passRetained({}({})).toOpaque())", - ty_name, args - ); + call_fn = format!("Unmanaged.passRetained({}({})).toOpaque()", ty_name, args); } else { call_fn = format!("{}::{}", ty_name, call_fn); } } else { - call_fn = built_in.convert_swift_expression_to_ffi_compatible( + call_fn = built_in.convert_swift_expression_to_ffi_type( &call_fn, TypePosition::FnReturn(func.host_lang), ); @@ -273,9 +270,10 @@ fn gen_function_exposes_swift_to_rust( let ret_value = format!( "__swift_bridge__{maybe_associated_ty}${fn_name}$param{idx}(ptr{swift_ffi_call_args})" ); - let ret_value = boxed_fn.ret.convert_swift_expression_to_ffi_compatible( + let ret_value = boxed_fn.ret.convert_ffi_expression_to_swift_type( &ret_value, - TypePosition::FnReturn(HostLang::Swift), + TypePosition::FnReturn(HostLang::Rust), + types, ); let maybe_generics = boxed_fn.maybe_swift_generics(); @@ -598,8 +596,8 @@ func __swift_bridge__Foo__free (ptr: UnsafeMutableRawPointer) { let expected = r#" @_cdecl("__swift_bridge__$Foo$new") -func __swift_bridge__Foo_new (_ a: UInt8) -> __private__PointerToSwiftType { - __private__PointerToSwiftType(ptr: Unmanaged.passRetained(Foo(a: a)).toOpaque()) +func __swift_bridge__Foo_new (_ a: UInt8) -> UnsafeMutableRawPointer { + Unmanaged.passRetained(Foo(a: a)).toOpaque() } "#; @@ -957,7 +955,7 @@ func __swift_bridge__void_pointer (_ arg: UnsafeRawPointer) { let expected = r#" func some_function(_ arg: Foo) { - __swift_bridge__$some_function(__private__PointerToSwiftType(ptr: Unmanaged.passRetained(arg).toOpaque())) + __swift_bridge__$some_function(Unmanaged.passRetained(arg).toOpaque()) } "#; @@ -984,7 +982,7 @@ func some_function(_ arg: Foo) { let expected = r#" func some_function() -> Foo { - Unmanaged.fromOpaque(__swift_bridge__$some_function().ptr).takeRetainedValue() + Unmanaged.fromOpaque(__swift_bridge__$some_function()).takeRetainedValue() } "#; 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 2a928199..14d477fb 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,4 +1,4 @@ -use crate::bridged_type::{fn_arg_name, BridgedType, StdLibType, TypePosition}; +use crate::bridged_type::{fn_arg_name, BridgeableType, BridgedType, StdLibType, TypePosition}; use crate::parse::{HostLang, TypeDeclaration}; use crate::{ParsedExternFn, TypeDeclarations, SWIFT_BRIDGE_PREFIX}; use quote::ToTokens; @@ -114,7 +114,7 @@ pub(super) fn gen_func_swift_calls_rust( } else { let ty = ty.to_token_stream().to_string(); format!( - "Unmanaged<{}>.fromOpaque({}.ptr).takeRetainedValue()", + "Unmanaged<{}>.fromOpaque({}).takeRetainedValue()", ty, call_rust ) } @@ -125,8 +125,9 @@ pub(super) fn gen_func_swift_calls_rust( } }; - let returns_null = Some(BridgedType::StdLib(StdLibType::Null)) - == BridgedType::new_with_return_type(&function.func.sig.output, types); + let returns_null = BridgedType::new_with_return_type(&function.func.sig.output, types) + .map(|b| b.is_null()) + .unwrap_or(false); let maybe_return = if returns_null || function.is_swift_initializer { "" @@ -156,9 +157,7 @@ pub(super) fn gen_func_swift_calls_rust( call_rust = call_rust ); } - BridgedType::StdLib(StdLibType::Option(briged_opt)) - if briged_opt.ty.deref() == &BridgedType::StdLib(StdLibType::Str) => - { + BridgedType::StdLib(StdLibType::Option(briged_opt)) if briged_opt.ty.is_str() => { call_rust = format!( r#"{maybe_return}optionalRustStrToRustStr({arg}, {{ {arg}AsRustStr in {indentation} {call_rust} 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 028a12f5..6c8077ae 100644 --- a/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs +++ b/crates/swift-bridge-ir/src/parse/parse_extern_mod.rs @@ -1,5 +1,7 @@ pub(crate) use self::opaque_type_attributes::OpaqueTypeAllAttributes; -use crate::bridged_type::{pat_type_pat_is_self, BridgedType}; +use crate::bridged_type::{ + bridgeable_type_from_fn_arg, pat_type_pat_is_self, BridgeableType, BridgedType, +}; use crate::errors::{FunctionAttributeParseError, IdentifiableParseError, ParseError, ParseErrors}; use crate::parse::parse_extern_mod::function_attributes::FunctionAttributes; use crate::parse::parse_extern_mod::generics::GenericOpaqueType; @@ -7,7 +9,7 @@ use crate::parse::type_declarations::{ OpaqueForeignTypeDeclaration, TypeDeclaration, TypeDeclarations, }; use crate::parse::{HostLang, OpaqueRustTypeGenerics}; -use crate::parsed_extern_fn::{fn_arg_is_mutable_reference, fn_arg_is_opaque_copy_type}; +use crate::parsed_extern_fn::fn_arg_is_mutable_reference; use crate::ParsedExternFn; use quote::ToTokens; use std::cmp::Ordering; @@ -64,13 +66,15 @@ impl<'a> ForeignModParser<'a> { let ty_name = foreign_ty.ident.to_string(); - if let Some(_builtin) = BridgedType::new_with_str( + if let Some(ty) = BridgedType::new_with_str( &foreign_ty.ident.to_string(), &self.type_declarations, ) { - self.errors.push(ParseError::DeclaredBuiltInType { - ty: foreign_ty.clone(), - }); + if ty.is_built_in_type() { + self.errors.push(ParseError::DeclaredBuiltInType { + ty: foreign_ty.clone(), + }); + } } let foreign_type = OpaqueForeignTypeDeclaration { @@ -160,11 +164,16 @@ impl<'a> ForeignModParser<'a> { for arg in func.sig.inputs.iter() { let is_mutable_ref = fn_arg_is_mutable_reference(arg); - let is_copy_opaque_type = fn_arg_is_opaque_copy_type( - &associated_type, - arg, - &self.type_declarations, - ); + let is_copy_opaque_type = + if let Some(TypeDeclaration::Opaque(o)) = associated_type.as_ref() { + o.attributes.copy.is_some() + } else if let Some(ty) = + bridgeable_type_from_fn_arg(arg, &self.type_declarations) + { + ty.has_swift_bridge_copy_annotation() + } else { + false + }; if is_mutable_ref && is_copy_opaque_type { self.errors diff --git a/crates/swift-bridge-ir/src/parse/type_declarations.rs b/crates/swift-bridge-ir/src/parse/type_declarations.rs index 291a8197..7678513d 100644 --- a/crates/swift-bridge-ir/src/parse/type_declarations.rs +++ b/crates/swift-bridge-ir/src/parse/type_declarations.rs @@ -51,18 +51,25 @@ impl TypeDeclaration { variants: shared_enum.variants.clone(), }))) } - TypeDeclaration::Opaque(opaque) => { - BridgedType::Foreign(CustomBridgedType::Opaque(OpaqueForeignType { - ty: opaque.ty.clone(), - host_lang: opaque.host_lang, - reference, - mutable, - has_swift_bridge_copy_annotation: opaque.attributes.copy.is_some(), - generics: opaque.generics.clone(), - })) + TypeDeclaration::Opaque(_o) => { + BridgedType::Bridgeable(Box::new(self.to_opaque_type(reference, mutable).unwrap())) } } } + + pub fn to_opaque_type(&self, reference: bool, mutable: bool) -> Option { + match self { + TypeDeclaration::Opaque(opaque) => Some(OpaqueForeignType { + ty: opaque.ty.clone(), + host_lang: opaque.host_lang, + reference, + mutable, + has_swift_bridge_copy_annotation: opaque.attributes.copy.is_some(), + generics: opaque.generics.clone(), + }), + _ => None, + } + } } #[derive(Clone)] diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn.rs b/crates/swift-bridge-ir/src/parsed_extern_fn.rs index 3bec7741..9562d78f 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn.rs @@ -1,5 +1,5 @@ -use crate::bridged_type::boxed_fn::BuiltInBoxedFnOnce; -use crate::bridged_type::{pat_type_pat_is_self, BridgedType, CustomBridgedType, StdLibType}; +use crate::bridged_type::boxed_fn::BridgeableBoxedFnOnce; +use crate::bridged_type::{pat_type_pat_is_self, BridgedType, StdLibType}; use crate::parse::{HostLang, SharedTypeDeclaration, TypeDeclaration, TypeDeclarations}; use crate::SWIFT_BRIDGE_PREFIX; use proc_macro2::{Ident, TokenStream}; @@ -272,7 +272,7 @@ impl ParsedExternFn { if let Some(built_in) = BridgedType::new_with_type(&pat_ty.ty, types) { if self.host_lang.is_rust() { - arg = built_in.convert_ffi_value_to_rust_value( + arg = built_in.convert_ffi_expression_to_rust_type( &arg, pat_ty.ty.span(), swift_bridge_path, @@ -285,7 +285,7 @@ impl ParsedExternFn { }; } } else { - arg = built_in.convert_rust_value_to_ffi_compatible_value( + arg = built_in.convert_rust_expression_to_ffi_type( &arg, swift_bridge_path, types, @@ -364,7 +364,7 @@ impl ParsedExternFn { if opaque.host_lang.is_rust() { "void*".to_string() } else { - "struct __private__PointerToSwiftType".to_string() + "void*".to_string() } } } @@ -378,7 +378,7 @@ impl ParsedExternFn { if let ReturnType::Type(_, ty) = &self.func.sig.output { if let Some(ty) = BridgedType::new_with_type(&ty, types) { - if let Some(include) = ty.c_include() { + if let Some(include) = ty.to_c_include() { includes.push(include); } } @@ -387,7 +387,7 @@ impl ParsedExternFn { for param in &self.func.sig.inputs { if let FnArg::Typed(pat_ty) = param { if let Some(ty) = BridgedType::new_with_type(&pat_ty.ty, types) { - if let Some(include) = ty.c_include() { + if let Some(include) = ty.to_c_include() { includes.push(include); } } @@ -458,7 +458,7 @@ impl ParsedExternFn { /// Generates something like: /// void __swift_bridge__$some_function$param0(void* boxed_fn, uint8_t arg); /// void __swift_bridge__$some_function$_free$param0(void* boxed_fn); - pub fn boxed_fn_to_c_header_fns(&self, idx: usize, boxed_fn: &BuiltInBoxedFnOnce) -> String { + pub fn boxed_fn_to_c_header_fns(&self, idx: usize, boxed_fn: &BridgeableBoxedFnOnce) -> String { let call_boxed_fn_link_name = self.call_boxed_fn_link_name(idx); let free_boxed_fn_link_name = self.free_boxed_fn_link_name(idx); @@ -516,7 +516,7 @@ void {free_boxed_fn_link_name}(void* {boxed_fn_arg_name});"# pub fn args_filtered_to_boxed_fns( &self, type_decls: &TypeDeclarations, - ) -> Vec<(usize, BuiltInBoxedFnOnce)> { + ) -> Vec<(usize, BridgeableBoxedFnOnce)> { self.func .sig .inputs @@ -640,31 +640,6 @@ pub(crate) fn fn_arg_is_mutable_reference(fn_arg: &FnArg) -> bool { } } -pub(crate) fn fn_arg_is_opaque_copy_type( - associated: &Option, - fn_arg: &FnArg, - types: &TypeDeclarations, -) -> bool { - match fn_arg { - FnArg::Receiver(_) => { - if let Some(TypeDeclaration::Opaque(opaque)) = associated.as_ref() { - opaque.attributes.copy.is_some() - } else { - false - } - } - FnArg::Typed(_) => { - if let Some(BridgedType::Foreign(CustomBridgedType::Opaque(opaque))) = - BridgedType::new_with_fn_arg(fn_arg, types) - { - opaque.has_swift_bridge_copy_annotation - } else { - false - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn/to_extern_c_fn.rs b/crates/swift-bridge-ir/src/parsed_extern_fn/to_extern_c_fn.rs index ac3f0434..44d32c13 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn/to_extern_c_fn.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn/to_extern_c_fn.rs @@ -53,7 +53,7 @@ impl ParsedExternFn { } else { let (await_fut, call_callback) = if maybe_return_ty.is_some() { let return_ty = self.return_ty_built_in(types).unwrap(); - let awaited_val = return_ty.convert_rust_value_to_ffi_compatible_value( + let awaited_val = return_ty.convert_rust_expression_to_ffi_type( "e! {fut.await}, swift_bridge_path, types, @@ -149,11 +149,8 @@ impl ParsedExternFn { // Async functions get this conversion done after awaiting the returned future. if self.sig.asyncness.is_none() { - call_fn = return_ty.convert_rust_value_to_ffi_compatible_value( - &call_fn, - swift_bridge_path, - types, - ); + call_fn = + return_ty.convert_rust_expression_to_ffi_type(&call_fn, swift_bridge_path, types); } call_fn diff --git a/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs b/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs index 951f7060..fcbd7dd2 100644 --- a/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs +++ b/crates/swift-bridge-ir/src/parsed_extern_fn/to_rust_impl_call_swift.rs @@ -64,7 +64,7 @@ impl ParsedExternFn { }; if let Some(built_in) = BridgedType::new_with_return_type(&sig.output, types) { - inner = built_in.convert_ffi_value_to_rust_value( + inner = built_in.convert_ffi_expression_to_rust_type( &inner, sig.output.span(), swift_bridge_path, @@ -147,7 +147,7 @@ impl ParsedExternFn { let call_boxed_fn = quote! { unsafe { Box::from_raw(#arg_name)(#(#call_args),*) } }; - let call_boxed_fn = boxed_fn.ret.convert_rust_value_to_ffi_compatible_value( + let call_boxed_fn = boxed_fn.ret.convert_rust_expression_to_ffi_type( &call_boxed_fn, swift_bridge_path, types, 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 3835ee6a..a26fc8bf 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 @@ -92,7 +92,7 @@ impl ParsedExternFn { let arg = if let Some(bridged_ty) = BridgedType::new_with_type(&pat_ty.ty, types) { if self.host_lang.is_rust() { - bridged_ty.convert_swift_expression_to_ffi_compatible( + bridged_ty.convert_swift_expression_to_ffi_type( &arg, TypePosition::FnArg(self.host_lang, arg_idx), ) diff --git a/crates/swift-integration-tests/src/bool.rs b/crates/swift-integration-tests/src/bool.rs deleted file mode 100644 index fdf7ae7c..00000000 --- a/crates/swift-integration-tests/src/bool.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[swift_bridge::bridge] -mod ffi { - extern "Rust" { - fn rust_negate_bool(start: bool) -> bool; - - fn run_bool_test(); - } - - extern "Swift" { - #[swift_bridge(swift_name = "swiftNegateBool")] - fn swift_negate_bool(start: bool) -> bool; - } -} - -fn run_bool_test() { - assert_eq!(ffi::swift_negate_bool(true), false); - assert_eq!(ffi::swift_negate_bool(false), true); -} - -fn rust_negate_bool(start: bool) -> bool { - !start -} diff --git a/crates/swift-integration-tests/src/lib.rs b/crates/swift-integration-tests/src/lib.rs index 3eccc0f8..15ef2af1 100644 --- a/crates/swift-integration-tests/src/lib.rs +++ b/crates/swift-integration-tests/src/lib.rs @@ -2,12 +2,12 @@ mod expose_opaque_rust_type; mod import_opaque_swift_class; mod async_function; -mod bool; mod boxed_functions; mod conditional_compilation; mod generics; mod option; mod pointer; +mod primitive; mod result; mod rust_function_uses_opaque_swift_type; mod shared_types; diff --git a/crates/swift-integration-tests/src/primitive.rs b/crates/swift-integration-tests/src/primitive.rs new file mode 100644 index 00000000..dc64f275 --- /dev/null +++ b/crates/swift-integration-tests/src/primitive.rs @@ -0,0 +1,91 @@ +#[swift_bridge::bridge] +mod ffi { + extern "Rust" { + fn test_rust_calls_swift_primitives(); + + fn rust_double_u8(arg: u8) -> u8; + fn rust_double_i8(arg: i8) -> i8; + fn rust_double_u16(arg: u16) -> u16; + fn rust_double_i16(arg: i16) -> i16; + fn rust_double_u32(arg: u32) -> u32; + fn rust_double_i32(arg: i32) -> i32; + fn rust_double_u64(arg: u64) -> u64; + fn rust_double_i64(arg: i64) -> i64; + fn rust_double_f32(arg: f32) -> f32; + fn rust_double_f64(arg: f64) -> f64; + fn rust_negate_bool(arg: bool) -> bool; + } + + extern "Swift" { + fn swift_double_u8(arg: u8) -> u8; + fn swift_double_i8(arg: i8) -> i8; + fn swift_double_u16(arg: u16) -> u16; + fn swift_double_i16(arg: i16) -> i16; + fn swift_double_u32(arg: u32) -> u32; + fn swift_double_i32(arg: i32) -> i32; + fn swift_double_u64(arg: u64) -> u64; + fn swift_double_i64(arg: i64) -> i64; + fn swift_double_f32(arg: f32) -> f32; + fn swift_double_f64(arg: f64) -> f64; + fn swift_negate_bool(arg: bool) -> bool; + } +} + +fn test_rust_calls_swift_primitives() { + assert_eq!(ffi::swift_double_u8(5), 10); + assert_eq!(ffi::swift_double_i8(5), 10); + assert_eq!(ffi::swift_double_u16(5), 10); + assert_eq!(ffi::swift_double_i16(5), 10); + assert_eq!(ffi::swift_double_u32(5), 10); + assert_eq!(ffi::swift_double_i32(5), 10); + assert_eq!(ffi::swift_double_u64(5), 10); + assert_eq!(ffi::swift_double_i64(5), 10); + assert_eq!(ffi::swift_double_f32(5.), 10.); + assert_eq!(ffi::swift_double_f64(5.), 10.); + assert_eq!(ffi::swift_negate_bool(true), false); + assert_eq!(ffi::swift_negate_bool(false), true); +} + +fn rust_double_u8(arg: u8) -> u8 { + arg * 2 +} + +fn rust_double_i8(arg: i8) -> i8 { + arg * 2 +} + +fn rust_double_u16(arg: u16) -> u16 { + arg * 2 +} + +fn rust_double_i16(arg: i16) -> i16 { + arg * 2 +} + +fn rust_double_u32(arg: u32) -> u32 { + arg * 2 +} + +fn rust_double_i32(arg: i32) -> i32 { + arg * 2 +} + +fn rust_double_u64(arg: u64) -> u64 { + arg * 2 +} + +fn rust_double_i64(arg: i64) -> i64 { + arg * 2 +} + +fn rust_double_f32(arg: f32) -> f32 { + arg * 2. +} + +fn rust_double_f64(arg: f64) -> f64 { + arg * 2. +} + +fn rust_negate_bool(arg: bool) -> bool { + !arg +} diff --git a/crates/swift-integration-tests/src/result.rs b/crates/swift-integration-tests/src/result.rs index 0a1b106e..66faf669 100644 --- a/crates/swift-integration-tests/src/result.rs +++ b/crates/swift-integration-tests/src/result.rs @@ -7,6 +7,9 @@ mod ffi { fn rust_func_takes_result_opaque_rust( arg: Result, ); + fn rust_func_takes_result_opaque_swift( + arg: Result, + ); } extern "Rust" { @@ -15,6 +18,12 @@ mod ffi { #[swift_bridge(init)] fn new(val: u32) -> ResultTestOpaqueRustType; } + + extern "Swift" { + type ResultTestOpaqueSwiftType; + + fn val(&self) -> u32; + } } fn rust_func_takes_result_string(arg: Result) { @@ -41,6 +50,19 @@ fn rust_func_takes_result_opaque_rust( } } +fn rust_func_takes_result_opaque_swift( + arg: Result, +) { + match arg { + Ok(ok) => { + assert_eq!(ok.val(), 555) + } + Err(err) => { + assert_eq!(err.val(), 666) + } + } +} + pub struct ResultTestOpaqueRustType { val: u32, }