chinedufn / swift-bridge

swift-bridge facilitates Rust and Swift interop.
https://chinedufn.github.io/swift-bridge
Apache License 2.0
842 stars 62 forks source link

#[swift_bridge(already_declared)] fails when wrapping an enum in Optional #283

Open jnbooth opened 3 months ago

jnbooth commented 3 months ago

Minimal reproduction:


#[swift_bridge::bridge]
mod ffi_1 {
    enum SomeTransparentEnum {
        Variant,
    }
}

use ffi_1::SomeTransparentEnum;

fn some_function() -> Option<SomeTransparentEnum> {
    Some(SomeTransparentEnum::Variant)
}

#[swift_bridge::bridge]
mod ffi_2 {
    #[swift_bridge(already_declared)]
    enum SomeTransparentEnum {}

    extern "Rust" {
        fn some_function() -> Option<SomeTransparentEnum>;
    }
}

This fails to compile due to:

cannot find type __swift_bridge__Option_SomeTransparentEnum in this scope

chinedufn commented 3 months ago

Thanks for reporting this.

Problem

We generate a __swift_bridge__Option_MyEnumName type in the module that declares the enum.

https://github.com/chinedufn/swift-bridge/blob/53b93f47cc32d605377194a43f3164745cea6dd6/crates/swift-bridge-ir/src/codegen/generate_rust_tokens/shared_enum.rs#L204-L209

https://github.com/chinedufn/swift-bridge/blob/832498a8a822690e2ef61e5929a05d713ba2fbe8/crates/swift-bridge-ir/src/codegen/codegen_tests/transparent_enum_codegen_tests.rs#L203-L208

The #[already_declared] module is trying to make use of that type, but it isn't visible.

Solution

The module that contains the #[already_declared] enum needs to look for __swift_bridge__Option_MyEnumName in the super:: module.

Here are steps to solve this:


Add a new #[already_declared] codegen test module for an extern "Rust" function that returns an Option<AlreadyDeclaredEnum>

Similar to this but for an Option<SomeEnum> https://github.com/chinedufn/swift-bridge/blob/ecd3b974754e32fc951b8bc4098ecef84e974821/crates/swift-bridge-ir/src/codegen/codegen_tests/already_declared_attribute_codegen_tests.rs#L168-L232


Add an integration test for calling a Rust function that returns an Option<AlreadyDeclaredEnum>.

Similar to:

https://github.com/chinedufn/swift-bridge/blob/4fbd30f81085dc366bda3c0303eac66345de3ed9/SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/SharedEnumAttributeTests.swift#L24-L32

https://github.com/chinedufn/swift-bridge/blob/4fbd30f81085dc366bda3c0303eac66345de3ed9/crates/swift-integration-tests/src/enum_attributes/already_declared.rs#L35-L37


Update the codegen to use super::__swift_bridge__Option_SomeTransparentEnum when the enum is already_declared.

https://github.com/chinedufn/swift-bridge/blob/53b93f47cc32d605377194a43f3164745cea6dd6/crates/swift-bridge-ir/src/bridged_type/shared_enum.rs#L55-L62

pub fn ffi_option_name_tokens(&self) -> TokenStream {
    let maybe_super = if self.already_declared {
        "super::"
    } else {
    ""
    };

    let name = Ident::new(
        &format!("{maybe_super}{}Option_{}", SWIFT_BRIDGE_PREFIX, self.name),
        self.name.span(),
    );
    quote! { #name }
}