Skip to content

Commit

Permalink
Fix already_declared attribute for enums and structs (#226)
Browse files Browse the repository at this point in the history
When generating the rust function wrappers for an extern swift function with an already_declared struct/enum the generated code used a locally defined struct/enum with this patch we add `super` to that function signature.

An example of code that was previously not working:

```rs
#[swift_bridge::bridge]
mod ffi_i {
  enum Foo {
    Bar.
  }
}

use ffi_i::Foo;

#[swift_bridge::bridge]
mod ffi {
  #[swift_bridge(already_declared)]
  enum Foo {}
  
  extern "Rust" {}
  extern "Swift" {
    fn baz(a: Foo);
  }
}
```

Previously the genrated function would be something like:
```rs
mod ffi {
  fn baz(a: Foo) {
    // ...
  }
}
```

now it's

```rs
mod ffi {
  fn baz(a: super::Foo) {
    // ...
  }
}
```
  • Loading branch information
conectado authored May 27, 2023
1 parent 7c14ea0 commit ecd3b97
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ import Foundation
func extern_swift_enum_rename(arg: EnumRename) -> EnumRename {
arg
}

func swift_reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest {
arg
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class SharedEnumAttributeTests: XCTestCase {
}


/// Verify that we can call a function that uses a type that was already declared in a different bridge module.
/// Verify that we can call a rust function from swift that uses a type that was already declared in a different bridge module.
func testSharedEnumAlreadyDeclared() throws {
XCTAssertEqual(
reflect_already_declared_enum(
Expand All @@ -31,11 +31,16 @@ class SharedEnumAttributeTests: XCTestCase {
)
}

/// Verify that we can call a swift function from rust that uses a type that was already declared in a different bridge module.
func testSharedEnumAlreadyDeclared() throws {
test_rust_calls_swift_already_declared()
}

/// Verify that we can use the generated Debug impl.
func testSharedEnumDeriveDebug() throws {
let debugString = String(reflecting: DeriveDebugEnum.Variant)
XCTAssertEqual(debugString, "Variant")
}

}

2 changes: 1 addition & 1 deletion book/src/bridge-module/transparent-types/enums/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ mod ffi_2 {
// The `already_declared` indicates that instead of creating a new enum
// we should use super::SomeTransparentEnum;
#[swift_bridge(already_declared)]
enum SomeTransparentEnum;
enum SomeTransparentEnum {}

extern "Rust" {
fn some_function() -> SomeTransparentEnum;
Expand Down
20 changes: 16 additions & 4 deletions crates/swift-bridge-ir/src/bridged_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,14 +902,26 @@ impl BridgedType {
},
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(shared_struct))) => {
let ty_name = &shared_struct.name;
quote! {
#ty_name
if shared_struct.already_declared {
quote! {
super::#ty_name
}
} else {
quote! {
#ty_name
}
}
}
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Enum(shared_enum))) => {
let enum_name = &shared_enum.name;
quote! {
#enum_name
if shared_enum.already_declared {
quote! {
super::#enum_name
}
} else {
quote! {
#enum_name
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,35 @@ mod already_declared_struct {
struct FfiSomeType;

extern "Rust" {
fn some_function(arg: FfiSomeType) -> FfiSomeType;
fn rust_some_function(arg: FfiSomeType) -> FfiSomeType;
}

extern "Swift" {
fn swift_some_function(arg: FfiSomeType) -> FfiSomeType;
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::DoesNotContain(quote! {
struct FfiSomeType
})
ExpectedRustTokens::ContainsManyAndDoesNotContainMany {
contains: vec![
quote! {
pub extern "C" fn __swift_bridge__rust_some_function(arg: <super::FfiSomeType as swift_bridge::SharedStruct>::FfiRepr) -> <super::FfiSomeType as swift_bridge::SharedStruct>::FfiRepr {
super::rust_some_function(arg.into_rust_repr()).into_ffi_repr()
}
},
quote! {
extern "C" {
#[link_name = "__swift_bridge__$swift_some_function"]
fn __swift_bridge__swift_some_function(arg: <super::FfiSomeType as swift_bridge::SharedStruct>::FfiRepr) -> <super::FfiSomeType as swift_bridge::SharedStruct>::FfiRepr;
}
},
],
does_not_contain: vec![quote! {
struct FfiSomeType
}],
}
}

fn expected_swift_code() -> ExpectedSwiftCode {
Expand All @@ -129,7 +148,7 @@ mod already_declared_struct {
fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::ExactAfterTrim(
r#"
struct __swift_bridge__$FfiSomeType __swift_bridge__$some_function(struct __swift_bridge__$FfiSomeType arg);
struct __swift_bridge__$FfiSomeType __swift_bridge__$rust_some_function(struct __swift_bridge__$FfiSomeType arg);
"#,
)
}
Expand Down Expand Up @@ -157,19 +176,31 @@ mod already_declared_enum {
enum FfiSomeEnum {}

extern "Rust" {
fn some_function(arg: FfiSomeEnum) -> FfiSomeEnum;
fn rust_some_function(arg: FfiSomeEnum) -> FfiSomeEnum;
}

extern "Swift" {
fn swift_some_function(arg: FfiSomeEnum) -> FfiSomeEnum;
}
}
}
}

fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::ContainsManyAndDoesNotContainMany {
contains: vec![quote! {
pub extern "C" fn __swift_bridge__some_function(arg: <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr) -> <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr {
super::some_function(arg.into_rust_repr()).into_ffi_repr()
}
}],
contains: vec![
quote! {
pub extern "C" fn __swift_bridge__rust_some_function(arg: <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr) -> <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr {
super::rust_some_function(arg.into_rust_repr()).into_ffi_repr()
}
},
quote! {
extern "C" {
#[link_name = "__swift_bridge__$swift_some_function"]
fn __swift_bridge__swift_some_function(arg: <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr) -> <super::FfiSomeEnum as swift_bridge::SharedEnum>::FfiRepr;
}
},
],
does_not_contain: vec![quote! {
enum FfiSomeEnum
}],
Expand All @@ -183,7 +214,7 @@ mod already_declared_enum {
fn expected_c_header() -> ExpectedCHeader {
ExpectedCHeader::ExactAfterTrim(
r#"
struct __swift_bridge__$FfiSomeEnum __swift_bridge__$some_function(struct __swift_bridge__$FfiSomeEnum arg);
struct __swift_bridge__$FfiSomeEnum __swift_bridge__$rust_some_function(struct __swift_bridge__$FfiSomeEnum arg);
"#,
)
}
Expand Down
1 change: 1 addition & 0 deletions crates/swift-bridge-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn bridge(
let tokens = quote! {
#module
};

tokens.into()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,26 @@ mod ffi2 {
enum AlreadyDeclaredEnumTest {}

extern "Rust" {
fn reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest;
fn rust_reflect_already_declared_enum(
arg: AlreadyDeclaredEnumTest,
) -> AlreadyDeclaredEnumTest;
}

extern "Rust" {
fn test_rust_calls_swift_already_declared();
}

extern "Swift" {
fn swift_reflect_already_declared_enum(
arg: AlreadyDeclaredEnumTest,
) -> AlreadyDeclaredEnumTest;
}
}

fn reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest {
fn rust_reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest {
arg
}
fn test_rust_calls_swift_already_declared() {
let val = ffi2::swift_reflect_already_declared_enum(AlreadyDeclaredEnumTest::Variant);
assert!(matches!(val, AlreadyDeclaredEnumTest::Variant));
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@ mod ffi2 {
struct AlreadyDeclaredStructTest;

extern "Rust" {
fn reflect_already_declared_struct(
fn rust_reflect_already_declared_struct(
arg: AlreadyDeclaredStructTest,
) -> AlreadyDeclaredStructTest;
}

extern "Swift" {
fn swift_reflect_already_declared_struct(
arg: AlreadyDeclaredStructTest,
) -> AlreadyDeclaredStructTest;
}
}

fn reflect_already_declared_struct(arg: AlreadyDeclaredStructTest) -> AlreadyDeclaredStructTest {
fn rust_reflect_already_declared_struct(
arg: AlreadyDeclaredStructTest,
) -> AlreadyDeclaredStructTest {
arg
}

0 comments on commit ecd3b97

Please sign in to comment.