rust-lang / rust-lang/rust-bindgen

Wrong binding for `static constexpr` with clang v14 and newer

Open
#2,640 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

The generated bindings for some C++ constants (static constexpr) with newer clang versions (v14 and onwards) result in a linked static value, instead of a const value. For older versions (v11, v12, v13) it works as expected.

Test setup:

  • Ubuntu 22.04
  • clang versions 11 to 15 available in the apt repositories
  • bindgen v0.68.1
  • rust v1.65
Input C/C++ Header
// This is more-or-less how it's done in <cstdint>
typedef unsigned int uint32_t;
namespace std {
     using ::uint32_t;
 }

namespace foo {
    using uint32_t = std::uint32_t;
    static constexpr uint32_t MY_CONST_VALUE = 200;
}

This seems to be the minimum amount of typedefs needed to reproduce the issue.
For example, with static constexpr std::uint32_t MY_CONST_VALUE = 200; or removing the using uint32_t = std::uint32_t; line, it seems to work correctly again - but since in my project the code is inside a provided lib header, I cannot work around it.

Bindgen Invocation
    let bindings = bindgen::Builder::default()
        .header("header.h")
        .clang_arg("--std=c++11")
        .clang_arg("-x")
        .clang_arg("c++")
        .allowlist_item(".*_VALUE")
        .enable_cxx_namespaces()
        .generate()
        .expect("Unable to generate bindings");
Actual Results

With clang >v14 (I tested with clang v14 and v15), this is the result:

/* automatically generated by rust-bindgen 0.68.1 */

#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    pub mod foo {
        #[allow(unused_imports)]
        use self::super::super::root;
        extern "C" {
            #[link_name = "\u{1}_ZN3fooL14MY_CONST_VALUEE"]
            pub static MY_CONST_VALUE: u32;
        }
    }
}
Expected Results

I'd expect the static constexpr to result in a pub const in the bindings. And this is the result with clang v11, v12 and v13:

/* automatically generated by rust-bindgen 0.68.1 */

#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    pub mod foo {
        #[allow(unused_imports)]
        use self::super::super::root;
        pub const MY_CONST_VALUE: u32 = 200;
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal C++ example in header.h and the bindgen::Builder invocation, running it across the reported clang versions 11 through 15. Compare the generated binding for foo::MY_CONST_VALUE; done means newer clang versions produce the expected public Rust const with value 200 rather than a linked static.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, rust
Domain
compilers, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.