Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix already_declared attribute for enums and structs #226

Merged
merged 3 commits into from
May 27, 2023

Conversation

conectado
Copy link
Contributor

Hi! I encounted an issue while working with the crate while using already_declared:

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:

#[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:

mod ffi {
  fn baz(a: Foo) {
    // ...
  }
}

now it's

mod ffi {
  fn baz(a: super::Foo) {
    // ...
  }
}

I'm not sure if it fixes all edge cases but it's working for me locally.

Let me know if I there's something to change to get this merged or if I was just not using already_declared correctly 😅

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.
@chinedufn chinedufn changed the title fix a already_declared attribute for enums and structs Fix already_declared attribute for enums and structs May 16, 2023
Copy link
Owner

@chinedufn chinedufn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your first contribution!

Just need to add tests and then we can land this.


Can add an extern "Swift" function in here

#[swift_bridge::bridge]
mod ffi2 {
#[swift_bridge(already_declared)]
enum AlreadyDeclaredEnumTest {}
extern "Rust" {
fn reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest;
}
}

And in here

#[swift_bridge::bridge]
mod ffi2 {
#[swift_bridge(already_declared, swift_repr = "struct")]
struct AlreadyDeclaredStructTest;
extern "Rust" {
fn reflect_already_declared_struct(
arg: AlreadyDeclaredStructTest,
) -> AlreadyDeclaredStructTest;
}
}


And then you can add an extern "Swift" function to this codegen test

/// Verify that we do not re-declare an already defined enum.
mod already_declared_enum {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
#[swift_bridge(already_declared)]
enum FfiSomeEnum {}
extern "Rust" {
fn 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()
}
}],
does_not_contain: vec![quote! {
enum FfiSomeEnum
}],
}
}

and this one

/// Verify that we do not re-declare an already defined struct.
mod already_declared_struct {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
#[swift_bridge(already_declared, swift_repr = "struct")]
struct FfiSomeType;
extern "Rust" {
fn some_function(arg: FfiSomeType) -> FfiSomeType;
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::DoesNotContain(quote! {
struct FfiSomeType
})
}

In both of the tests you can use the ExpectedRustTokens::ContainsManyAndDoesNotContainMany to confirm that we generate code for the extern Swift function that uses super::TypeName

@conectado
Copy link
Contributor Author

Thanks for your first contribution!

Just need to add tests and then we can land this.

Can add an extern "Swift" function in here

#[swift_bridge::bridge]
mod ffi2 {
#[swift_bridge(already_declared)]
enum AlreadyDeclaredEnumTest {}
extern "Rust" {
fn reflect_already_declared_enum(arg: AlreadyDeclaredEnumTest) -> AlreadyDeclaredEnumTest;
}
}

And in here

#[swift_bridge::bridge]
mod ffi2 {
#[swift_bridge(already_declared, swift_repr = "struct")]
struct AlreadyDeclaredStructTest;
extern "Rust" {
fn reflect_already_declared_struct(
arg: AlreadyDeclaredStructTest,
) -> AlreadyDeclaredStructTest;
}
}

And then you can add an extern "Swift" function to this codegen test

/// Verify that we do not re-declare an already defined enum.
mod already_declared_enum {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
#[swift_bridge(already_declared)]
enum FfiSomeEnum {}
extern "Rust" {
fn 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()
}
}],
does_not_contain: vec![quote! {
enum FfiSomeEnum
}],
}
}

and this one

/// Verify that we do not re-declare an already defined struct.
mod already_declared_struct {
use super::*;
fn bridge_module_tokens() -> TokenStream {
quote! {
mod ffi {
#[swift_bridge(already_declared, swift_repr = "struct")]
struct FfiSomeType;
extern "Rust" {
fn some_function(arg: FfiSomeType) -> FfiSomeType;
}
}
}
}
fn expected_rust_tokens() -> ExpectedRustTokens {
ExpectedRustTokens::DoesNotContain(quote! {
struct FfiSomeType
})
}

In both of the tests you can use the ExpectedRustTokens::ContainsManyAndDoesNotContainMany to confirm that we generate code for the extern Swift function that uses super::TypeName

Thanks for the review and detailed guidance!

Added the tests :)

@conectado conectado requested a review from chinedufn May 16, 2023 20:33
Copy link
Owner

@chinedufn chinedufn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

extern "Swift" {
fn swift_reflect_already_declared_enum(
arg: AlreadyDeclaredEnumTest,
) -> AlreadyDeclaredEnumTest;
Copy link
Collaborator

@NiwakaDev NiwakaDev May 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chinedufn

I guess that this function isn't called from anywhere. What do you think about this?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah we should call it. Great point. Want to tell @conectado how to best call it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test here that I think does this.

I couldn't run the tests because I think the CI is failing so lmk if this work or I should do something else?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @conectado
The CI failure is due to #213. If you want to know more detail, please see #225.

I guess it should be fine as long as you run the test multiple times and then the test pass at least once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Locally it's consistently failing with:

Testing failed:
field has incomplete type 'struct swift_bridge$tuple$I32ResultTestOpaqueRustTypeString'
failed to emit precompiled header '/Users/conectado/Library/Developer/Xcode/DerivedData/SwiftRustIntegrationTestRunner-cocipvlohdpefifldegpkicaiuld/Build/Intermediates.noindex/PrecompiledHeaders/BridgingHeader-swift_4X3E80DLUM90-clang_KV4PBZK8M8GA.pch' for bridging header '/Users/conectado/repos/firezone/swift-bridge/SwiftRustIntegrationTestRunner/Headers/BridgingHeader.h'
Testing cancelled because the build failed

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is due to #213.

Locally it's consistently failing with:

It is not consistently. Right now, when we run ./test-swift-rust-integration.sh, The tests will sometimes fails. If you check #225, You could understand why the tests sometimes fails.

On the other hand, when we run cargo test --all, the tests will consistently pass.

@chinedufn
Copy link
Owner

Yeah CI is currently failing due to #225 (comment)

Test looks good, I'll land this. Thanks!

@chinedufn chinedufn merged commit ecd3b97 into chinedufn:master May 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants