rust-lang / rust-lang/rust-bindgen

Support for `std::underlying_type_t`

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

Nobody has claimed this yet.

A-C++ A-templates
Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

Input C/C++ Header
namespace std {
template <typename _Tp, _Tp __v> struct integral_constant {
  static constexpr _Tp value = __v;
  typedef _Tp value_type;
  typedef integral_constant<_Tp, __v> type;
  constexpr operator value_type() const noexcept { return value; }
#if __cplusplus > 201103L

#define __cpp_lib_integral_constant_callable 201304

  constexpr value_type operator()() const noexcept { return value; }
#endif
};

template <typename _Tp, _Tp __v>
constexpr _Tp integral_constant<_Tp, __v>::value;

typedef integral_constant<bool, true> true_type;

typedef integral_constant<bool, false> false_type;

template <bool __v> using __bool_constant = integral_constant<bool, __v>;

template <typename _Tp>
struct is_enum : public integral_constant<bool, __is_enum(_Tp)> {};

template <typename _Tp, bool = is_enum<_Tp>::value>
struct __underlying_type_impl {
  using type = __underlying_type(_Tp);
};

template <typename _Tp> struct __underlying_type_impl<_Tp, false> {};

template <typename _Tp>
struct underlying_type : public __underlying_type_impl<_Tp> {};

template <typename _Tp>
using underlying_type_t = typename underlying_type<_Tp>::type;
} // namespace std

enum MyEnum : int { A, B, C };

class EnumWrapper {
  std::underlying_type_t<MyEnum> inner;
};
Bindgen Invocation
bindgen::Builder::default()
        .clang_args(&["-std=c++20"])
        .allowlist_type("EnumWrapper")
        // The input header we would like to generate
        // bindings for.
        .header("header.hpp")
        .enable_cxx_namespaces()
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");
Actual Results
running 3 tests
test root::__bindgen_test_layout_underlying_type_t_open0_MyEnum_close0_instantiation ... FAILED
test root::bindgen_test_layout_EnumWrapper ... FAILED
test test ... ok

failures:

---- root::__bindgen_test_layout_underlying_type_t_open0_MyEnum_close0_instantiation stdout ----
thread 'root::__bindgen_test_layout_underlying_type_t_open0_MyEnum_close0_instantiation' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `4`: Size of template specialization: root :: std :: underlying_type_t', /run/media/aleok/data/programming/bindgen-mre/target/debug/build/bindgen-mre-b0317d4ce440433c/out/bindings.rs:52:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- root::bindgen_test_layout_EnumWrapper stdout ----
thread 'root::bindgen_test_layout_EnumWrapper' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `4`: Size of: EnumWrapper', /run/media/aleok/data/programming/bindgen-mre/target/debug/build/bindgen-mre-b0317d4ce440433c/out/bindings.rs:29:9

This is because the bindings are incorrect and underlying_type is incorrectly opaque:

/* automatically generated by rust-bindgen 0.59.2 */

#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    pub mod std {
        #[allow(unused_imports)]
        use self::super::super::root;
        pub type __underlying_type_impl_type<_Tp> = _Tp;
        #[repr(C)]
        #[derive(Debug, Copy, Clone)]
        pub struct underlying_type {
            pub _address: u8,
        }
        pub type underlying_type_t = root::std::underlying_type;
    }
    pub const MyEnum_A: root::MyEnum = 0;
    pub const MyEnum_B: root::MyEnum = 1;
    pub const MyEnum_C: root::MyEnum = 2;
    pub type MyEnum = ::std::os::raw::c_int;
    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct EnumWrapper {
        pub inner: root::std::underlying_type_t,
    }
    /* ... tests ... */
}
Expected Results
/* automatically generated by rust-bindgen 0.59.2 */

#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
pub mod root {
    #[allow(unused_imports)]
    use self::super::root;
    #[repr(C)]
    #[derive(Debug, Copy, Clone)]
    pub struct EnumWrapper {
        pub inner: root::EnumWrapper_type,
    }
    pub type EnumWrapper_type = ::std::os::raw::c_int;
}

Note that these are actually the bindings generated if we use std::underlying_type<MyEnum>::type instead of std::underlying_type_t<MyEnum>! The issue happens with std::underlying_type_t.

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

Reproduce the issue using header.hpp, the shown bindgen::Builder invocation, and -std=c++20. Compare generation for std::underlying_type_t with std::underlying_type::type, then verify that the generated EnumWrapper layout matches the expected c_int-sized result and the layout checks pass.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.