From b6e2012c032debed8ac2463d4d8114ab17242303 Mon Sep 17 00:00:00 2001 From: Koi Date: Tue, 3 Nov 2026 00:45:25 -0500 Subject: [PATCH] ir: Treat char8_t as a u8 builtin libclang has no CXType_Char8, so char8_t reaches build_builtin_ty as CXType_Unexposed and is not recognized. Array element types then fail to resolve and bindgen panics with "Not able to resolve array element?", and enums with a char8_t underlying type are emitted as signed. Map an unexposed type whose canonical spelling is char8_t to u8. Fixes #3474. Fixes #3464. --- CHANGELOG.md | 1 + .../tests/expectations/tests/char8_t.rs | 38 +++++++++++++++++++ bindgen-tests/tests/headers/char8_t.hpp | 18 +++++++++ bindgen/ir/context.rs | 11 ++++++ 4 files changed, 68 insertions(+) create mode 100644 bindgen-tests/tests/expectations/tests/char8_t.rs create mode 100644 bindgen-tests/tests/headers/char8_t.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index cb7c8a055b..2ffa09c743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -254,6 +254,7 @@ - Fixed a panic when generating bindings for Objective-C methods that declare C-style parameters (#3488). - Fixed a panic when an Objective-C method returns `Class`. The `Class` type is now generated as `*mut objc::runtime::Class` instead of an interface named after the enclosing declaration (#3484). - Fixed a stack overflow when generating bindings for nested alias template instantiations such as `U>` (#3478). +- Fixed a panic when generating bindings for C++20 `char8_t` arrays. `char8_t` is now always generated as `u8`, so enums with a `char8_t` underlying type are unsigned too (#3474, #3464). ## Security diff --git a/bindgen-tests/tests/expectations/tests/char8_t.rs b/bindgen-tests/tests/expectations/tests/char8_t.rs new file mode 100644 index 0000000000..9166ef3d69 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/char8_t.rs @@ -0,0 +1,38 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +unsafe extern "C" { + pub static mut global_array: [u8; 4usize]; +} +unsafe extern "C" { + pub static mut incomplete_array: [u8; 0usize]; +} +unsafe extern "C" { + pub static mut nested_array: [[u8; 2usize]; 2usize]; +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct WithChar8Array { + pub bytes: [u8; 8usize], + pub single: u8, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of WithChar8Array"][::std::mem::size_of::() - 9usize]; + ["Alignment of WithChar8Array"][::std::mem::align_of::() - 1usize]; + [ + "Offset of field: WithChar8Array::bytes", + ][::std::mem::offset_of!(WithChar8Array, bytes) - 0usize]; + [ + "Offset of field: WithChar8Array::single", + ][::std::mem::offset_of!(WithChar8Array, single) - 8usize]; +}; +unsafe extern "C" { + #[link_name = "\u{1}_Z16take_char8_arrayPDu"] + pub fn take_char8_array(input: *mut u8); +} +unsafe extern "C" { + #[link_name = "\u{1}_Z12return_char8Du"] + pub fn return_char8(input: u8) -> u8; +} +pub const Char8Enum_High: Char8Enum = 255; +pub const Char8Enum_Low: Char8Enum = 65; +pub type Char8Enum = u8; diff --git a/bindgen-tests/tests/headers/char8_t.hpp b/bindgen-tests/tests/headers/char8_t.hpp new file mode 100644 index 0000000000..11c5a020d4 --- /dev/null +++ b/bindgen-tests/tests/headers/char8_t.hpp @@ -0,0 +1,18 @@ +// bindgen-flags: -- -std=c++20 + +extern char8_t global_array[4]; +extern char8_t incomplete_array[]; +extern char8_t nested_array[2][2]; + +struct WithChar8Array { + char8_t bytes[8]; + char8_t single; +}; + +void take_char8_array(char8_t input[4]); +char8_t return_char8(char8_t input); + +enum class Char8Enum : char8_t { + High = 0xFF, + Low = 0x41, +}; diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 72419107c5..dc56c202b7 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -2024,6 +2024,17 @@ If you encounter an error missing from this list, please file an issue or a PR!" } CXType_Char16 => TypeKind::Int(IntKind::U16), CXType_Char32 => TypeKind::Int(IntKind::U32), + // libclang has no CXType_Char8, so char8_t shows up as unexposed. + CXType_Unexposed + if ty + .canonical_type() + .spelling() + .split_whitespace() + .filter(|part| !matches!(*part, "const" | "volatile")) + .eq(["char8_t"]) => + { + TypeKind::Int(IntKind::U8) + } CXType_Long => TypeKind::Int(IntKind::Long), CXType_ULong => TypeKind::Int(IntKind::ULong), CXType_LongLong => TypeKind::Int(IntKind::LongLong),