dtolnay / cxx

Safe interop between Rust and C++
https://cxx.rs
Apache License 2.0
5.68k stars 322 forks source link

Multiple mods in the same file creating a type alias for the same type raises "the name `...` is defined multiple times" error #1279

Open dpaoliello opened 8 months ago

dpaoliello commented 8 months ago

In Types::collect(), the cxx crate checks for duplicate definitions of Rust identifiers.

This works for the proc macro (which is run per mod) but causes issues for the build script as it operates on a file level, gathering all APIs across all mods in the file. Thus, if the same type alias is declared in multiple mods in the same file then the build script will raise an error despite there being no issue with being able to generate the final C++ code.

Example

For :

pub struct MyStruct {
  pub val: u32,
}

unsafe impl cxx::ExternType for MyStruct {
  type Id = cxx::type_id!("handle::ffi::MyStruct");
  type Kind = cxx::kind::Opaque;
}

#[cxx::bridge]
mod ffi1 {
    extern "C++" {
        type MyStruct = crate::MyStruct;
    }
}

#[cxx::bridge]
mod ffi2 {
    extern "C++" {
        type MyStruct = crate::MyStruct;
    }
}

Building this will cause an error to be raised

  error[cxxbridge]: the name `MyStruct` is defined multiple times
     ┌─ src/lib.rs:20:9
     │
  20 │         type MyStruct = crate::MyStruct;
     │         ^^^^^^^^^^^^^ the name `MyStruct` is defined multiple times

Why do you need this?

APIs in an extern "C++" block may only reference types that are built-in to cxx and other types in the same mod, thus it is pretty reasonable to need to access the same type via an alias across multiple mods in the same file.

Possible fixes

The simplest fix would be to disable checking when calling Types::collect from the build script - but this would drop other checks that may still be valuable.

I believe the correct fix is to use a different set of checking logic instead - the current logic is only looking at Rust names and so isn't validating that the C++ names being emitted are not duplicated. Types::collect should add a parameter to indicate if the Rust or C++ names should be checked, and then perform the appropriate checks.