Skip to content

the name <> is defined multiple times caused by enum and integer definition #2008

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

Closed
toger5 opened this issue Mar 12, 2021 · 3 comments · Fixed by #2326
Closed

the name <> is defined multiple times caused by enum and integer definition #2008

toger5 opened this issue Mar 12, 2021 · 3 comments · Fixed by #2326

Comments

@toger5
Copy link

toger5 commented Mar 12, 2021

Input C/C++ Header

#define spectre_enum(_type, _name) _type _name; enum _name
typedef spectre_enum( unsigned int, SpectreAlgorithm ) {
    /** (2012-03-05) V0 incorrectly performed host-endian math with bytes translated into 16-bit network-endian. */
    SpectreAlgorithmV0,
    /** (2012-07-17) V1 incorrectly sized site name fields by character count rather than byte count. */
    SpectreAlgorithmV1,
    /** (2014-09-24) V2 incorrectly sized user name fields by character count rather than byte count. */
    SpectreAlgorithmV2,
    /** (2015-01-15) V3 is the current version. */
    SpectreAlgorithmV3,

    SpectreAlgorithmCurrent = SpectreAlgorithmV3,
    SpectreAlgorithmFirst = SpectreAlgorithmV0,
    SpectreAlgorithmLast = SpectreAlgorithmV3,
};

Bindgen Invocation

Builder command:

let bindings = bindgen::Builder::default()
        .header("src/spectre/api/c/spectre-types.h")
        .header("src/spectre/api/c/spectre-marshal.h")
        .header("src/spectre/api/c/spectre-algorithm.h")
        // .constified_enum_module("*")
        .rustfmt_bindings(true)
        .generate()
        .expect("Unable to generate bindings");

Actual Results

Produced bindings.rs

pub type SpectreAlgorithm = ::std::os::raw::c_uint; // <<<<<< HERE
#[doc = " (2012-03-05) V0 incorrectly performed host-endian math with bytes translated into 16-bit network-endian."]
pub const SpectreAlgorithm_SpectreAlgorithmV0: SpectreAlgorithm = 0;
#[doc = " (2012-07-17) V1 incorrectly sized site name fields by character count rather than byte count."]
pub const SpectreAlgorithm_SpectreAlgorithmV1: SpectreAlgorithm = 1;
#[doc = " (2014-09-24) V2 incorrectly sized user name fields by character count rather than byte count."]
pub const SpectreAlgorithm_SpectreAlgorithmV2: SpectreAlgorithm = 2;
#[doc = " (2015-01-15) V3 is the current version."]
pub const SpectreAlgorithm_SpectreAlgorithmV3: SpectreAlgorithm = 3;
#[doc = " (2015-01-15) V3 is the current version."]
pub const SpectreAlgorithm_SpectreAlgorithmCurrent: SpectreAlgorithm = 3;
#[doc = " (2015-01-15) V3 is the current version."]
pub const SpectreAlgorithm_SpectreAlgorithmFirst: SpectreAlgorithm = 0;
#[doc = " (2015-01-15) V3 is the current version."]
pub const SpectreAlgorithm_SpectreAlgorithmLast: SpectreAlgorithm = 3;
#[doc = " Types."]
pub type SpectreAlgorithm = ::std::os::raw::c_uint; // <<<<<< and HERE

Error from cargo:

error[E0428]: the name `SpectreAlgorithm` is defined multiple times
    --> /home/timo/Projects/rust-gtk-mpw/target/debug/build/rust-mpw-a3a9037bf1230854/out/spectre_bindings.rs:2327:1
     |
2311 | pub type SpectreAlgorithm = ::std::os::raw::c_uint;
     | --------------------------------------------------- previous definition of the type `SpectreAlgorithm` here
...
2327 | pub type SpectreAlgorithm = ::std::os::raw::c_uint;
     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SpectreAlgorithm` redefined here
     |
     = note: `SpectreAlgorithm` must be defined only once in the type namespace of this module

Expected Results

The bindgen should just irgnore the duplicate definition of the SpectreAlgorithm type. In c the enum and the integer are used for easier function signatures.

@emilio
Copy link
Contributor

emilio commented Apr 3, 2021

That C code complains here:

Definition of type 'SpectreAlgorithm' conflicts with typedef of the same name

I wonder why they didn't use the more common and portable:

typedef enum SpectreAlgorithm {
  // ...
} SpectreAlgorithm;

Anyhow bindgen could probably be taught about this pattern.

@toger5
Copy link
Author

toger5 commented Apr 3, 2021

@emilio This is also the solution I went with now. Changing the C code to only declare it once.

Here is a thread with the author of the code code explaining the current implementation. https://chat.spectre.app/d/6-issue-compiling-the-spectre-api/7

From my side the issue is solved. It still would be great if bindgen handles this out of the box. Should I keep the issue open?

@dtolnay
Copy link
Member

dtolnay commented Oct 26, 2022

This is a common idiom in C. cbindgen even uses it — see https://github.com/eqrion/cbindgen/blob/v0.24.3/src/bindgen/ir/enumeration.rs#L742.

$ cbindgen --lang c <(echo '
    #[repr(i16)]
    pub enum Asdf { Jkl }

    #[no_mangle]
    pub extern "C" fn f(asdf: Asdf) {}
')
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

enum Asdf {
  Jkl,
};
typedef int16_t Asdf;

void f(Asdf asdf);

@emilio if you were seeing a "conflicts with typedef" error, it's because you must have been using a C++ compiler. This code is valid and idiomatic in C.

justsmth pushed a commit to justsmth/rust-bindgen that referenced this issue Nov 28, 2022
Fixes rust-lang#2008.

Example:

```c
enum Enum { Variant };
typedef int16_t Enum;
```

This is valid and idiomatic C (though not valid C++). `cbindgen` uses this idiom as the default C translation of Rust enums, the equivalent of what would be `enum Enum : int16_t { Variant };` in C++.

`bindgen header.h` before:

```rust
pub const Enum_Variant: Enum = 0;
pub type Enum = ::std::os::raw::c_uint;
pub type Enum = i16;
```

```console
error[E0428]: the name `Enum` is defined multiple times
 --> generated.rs:3:1
  |
2 | pub type Enum = ::std::os::raw::c_uint;
  | --------------------------------------- previous definition of the type `Enum` here
3 | pub type Enum = i16;
  | ^^^^^^^^^^^^^^^^^^^^ `Enum` redefined here
  |
  = note: `Enum` must be defined only once in the type namespace of this module
```

After:

```rust

pub const Enum_Variant: Enum = 0;
pub type Enum = i16;
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants