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

Issue using only a few types from a uniffi dependency in a uniffi library #2282

Open
thunderbiscuit opened this issue Oct 22, 2024 · 4 comments

Comments

@thunderbiscuit
Copy link

Hi there. I maintain a uniffi-based library which we have recently broken into two (I'll call them main and low-level-types). Our main library imports some of the types defined in this lower-level library we maintain to offer those lower-level types to other libraries that use uniffi.

I recently found an issue where types defined in my main library that are also defined in the lowe-level-types dependency name clash in Swift because Swift imports all types at once. This lead me to look into the glue code generated for the lower-level dependency, and notice that all types in that library are added to the glue code even though our library only imports some of them.

I'm wondering if I am maybe not configuring the dependency correctly, or if there is something I misunderstood.

Here is an example udl from the lower-level-types library:

interface Script {
  constructor(sequence<u8> raw_output_script); 
  sequence<u8> to_bytes();
};

dictionary OutPoint {
  Txid txid;
  u32 vout;
};

And our use of the Script type in the main library:

[ExternalInterface="core_types_ffi"]
typedef extern Script;

Again our issue is that the OutPoint type will appear in the glue code for the lower-level-types even though we do not need it there. Moreover, because we do have an OutPoint type in the main library, the two Outpoints will clash and runtime tests/code will fail.

@mhammond
Copy link
Member

This does seem like a bug, but I can't repro it. eg, in our repo and the following patch:

--- a/fixtures/ext-types/uniffi-one/src/lib.rs
+++ b/fixtures/ext-types/uniffi-one/src/lib.rs
@@ -49,4 +49,9 @@
     fn hello(&self) -> String;
 }
 
+// A "local" type - not referenced by consumers of our crate,
+// so we test support for it is *not* generated for those consumers.
+#[derive(uniffi::Record)]
+pub struct LocalType {}
+
 uniffi::include_scaffolding!("uniffi-one");

This is your "low level" crate. I run the command cargo test -p uniffi-fixture-ext-types (which is your main crate), and the generated code doesn't reference the type. If I add the following patch:

--- a/fixtures/ext-types/lib/src/lib.rs
+++ b/fixtures/ext-types/lib/src/lib.rs
@@ -203,4 +203,8 @@
     t
 }
 
+#[uniffi::export]
+fn get_local_type(_t: uniffi_one::LocalType) {
+}
+
 uniffi::include_scaffolding!("ext-types-lib");

(which is your "main" crate, the crate we are testing) it does.

@thunderbiscuit
Copy link
Author

thunderbiscuit commented Nov 4, 2024

I wonder if it's because you are using the proc macros?

I made a smaller reproducible example here: uniffi/2282.

My calendar-ffi library imports a single type from the lower level clock-ffi library, the ClockType enum. You can see the UDL file here. The generated glue code for the clock-ffi (clock.kt), however, also contains the OtherTypeNotUseful enum, which was not imported at all. It's as if the glue code file generated for the lower level library builds all of the UDL file regardless of what is imported at the higher level lib.

I only realized this because one of our types was defined at both layers (but the higher-level library did not import the low-level one) and that created issues for Swift because in Swift all imports are imported at once and therefore we were getting a name clash.

@mhammond
Copy link
Member

mhammond commented Nov 4, 2024

I don't think procmacros would make a difference here, but both those crates I mentioned above do have udl files.

@thunderbiscuit
Copy link
Author

thunderbiscuit commented Dec 10, 2024

Hi Mark! Sorry it took me a while to get back to this. I was able to reproduce my bug using the fixtures.

See the diff here. I basically simply removed the import of the UniffiOneEnum in the ext-types library udl (this type is imported from and defined in the uniffi-one dependency.

// [External="uniffi_one"]
// typedef extern UniffiOneEnum;

From there I built the glue code like so:

cd ./fixtures/ext-types/
mkdir temp
cargo build
cargo run --bin uniffi-bindgen generate --library ../../target/debug/libuniffi_ext_types_lib.dylib --language kotlin --out-dir ./temp/ --no-format

If you build the glue code with the import of the UniffiOneEnum type removed from the udl, you still get the type defined in temp/uniffi/uniffi_one_ns/uniffi_one_ns.kt:

enum class UniffiOneEnum {
   
    ONE,
    TWO;
    companion object
}

This is odd because your file should only have the types you asked for in your UDL as far as I understand it.

Issue

Even if you have a UniffiOneEnum type defined in your main crate (maybe because you don't like the one defined in the dependency), it will not create too much trouble in Kotlin and Python because those types are namespaced separately, but in Swift you'll get a compile time error if you try to build your project, because Swift imports all symbols at once and at the root level I think? In any case the two UniffiOneEnum will clash.

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

No branches or pull requests

2 participants