From 92190ece39c5637c0ca194be3f5635b99eb40b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Jun 2026 20:25:58 +0200 Subject: [PATCH 1/5] ir: Use Path for ConstExprs. This will be useful to support associated constants. --- src/bindgen/ir/generic_path.rs | 2 +- src/bindgen/ir/ty.rs | 28 ++++++++++++---------------- src/bindgen/mangle.rs | 12 +++++++----- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index c8af0e4b7..cf78d40dd 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -229,7 +229,7 @@ impl GenericArgument { pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { match *self { GenericArgument::Type(ref mut ty) => ty.rename_for_config(config, generic_params), - GenericArgument::Const(ref mut expr) => expr.rename_for_config(config), + GenericArgument::Const(ref mut expr) => expr.rename_for_config(config, generic_params), } } } diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index d54620b5f..debd126da 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -237,20 +237,21 @@ impl PrimitiveType { /// limited vocabulary here: only identifiers and literals. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ConstExpr { - Name(String), + Path(GenericPath), Value(String), } impl ConstExpr { pub fn as_str(&self) -> &str { match *self { - ConstExpr::Name(ref string) | ConstExpr::Value(ref string) => string, + ConstExpr::Path(ref path) => path.export_name(), + ConstExpr::Value(ref string) => string, } } - pub fn rename_for_config(&mut self, config: &Config) { - if let ConstExpr::Name(ref mut name) = self { - config.export.rename(name); + pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) { + if let ConstExpr::Path(ref mut path) = self { + path.rename_for_config(config, generic_params); } } @@ -266,27 +267,23 @@ impl ConstExpr { }; Ok(ConstExpr::Value(val)) } - syn::Expr::Path(ref path) => { - let generic_path = GenericPath::load(&path.path)?; - Ok(ConstExpr::Name(generic_path.export_name().to_owned())) - } + syn::Expr::Path(ref path) => Ok(ConstExpr::Path(GenericPath::load(&path.path)?)), syn::Expr::Cast(ref cast) => Ok(ConstExpr::load(&cast.expr)?), _ => Err(format!("can't handle const expression {expr:?}")), } } pub fn specialize(&self, mappings: &[(&Path, &GenericArgument)]) -> ConstExpr { - match *self { - ConstExpr::Name(ref name) => { - let path = Path::new(name); + if let ConstExpr::Path(ref path) = *self { + if path.is_single_identifier() { for &(param, value) in mappings { - if path == *param { + if *param == *path.path() { match *value { GenericArgument::Type(Type::Path(ref path)) if path.is_single_identifier() => { // This happens when the generic argument is a path. - return ConstExpr::Name(path.name().to_string()); + return ConstExpr::Path(path.clone()); } GenericArgument::Const(ref expr) => { return expr.clone(); @@ -298,7 +295,6 @@ impl ConstExpr { } } } - ConstExpr::Value(_) => {} } self.clone() } @@ -766,7 +762,7 @@ impl Type { Type::Primitive(_) => {} Type::Array(ref mut ty, ref mut len) => { ty.rename_for_config(config, generic_params); - len.rename_for_config(config); + len.rename_for_config(config, generic_params); } Type::FuncPtr { ref mut ret, diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs index 5f7fa0969..01812e9bf 100644 --- a/src/bindgen/mangle.rs +++ b/src/bindgen/mangle.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::bindgen::config::MangleConfig; -use crate::bindgen::ir::{ConstExpr, GenericArgument, GenericPath, Path, Type}; +use crate::bindgen::ir::{ConstExpr, GenericArgument, Path, Type}; use crate::bindgen::rename::IdentifierType; pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path { @@ -73,12 +73,11 @@ impl<'a> Mangler<'a> { fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) { match *arg { GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last), - GenericArgument::Const(ConstExpr::Name(ref name)) => { + GenericArgument::Const(ConstExpr::Path(ref path)) => { // This must behave the same as a GenericArgument::Type, // because const arguments are commonly represented as Types; // see the comment on `enum GenericArgument`. - let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![])); - self.append_mangled_type(&fake_ty, last); + self.append_mangled_type(&Type::Path(path.clone()), last); } GenericArgument::Const(ConstExpr::Value(ref val)) => self.output.push_str(val), } @@ -320,7 +319,10 @@ fn generics() { assert_eq!( mangle_path( &Path::new("Top"), - &[GenericArgument::Const(ConstExpr::Name("N".to_string()))], + &[GenericArgument::Const(ConstExpr::Path(GenericPath::new( + Path::new("N"), + vec![], + )))], &MangleConfig::default(), ), Path::new("Top_N") From bdbcbd049ec54fcebadbf89bf227f8421f69cdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Jun 2026 19:17:38 +0200 Subject: [PATCH 2/5] ir: Allow constant literals with enum variants. --- src/bindgen/bindings.rs | 39 ++++++++++++++++++++- src/bindgen/builder.rs | 1 + src/bindgen/ir/constant.rs | 1 + src/bindgen/language_backend/clike.rs | 3 ++ src/bindgen/language_backend/cython.rs | 3 ++ src/bindgen/library.rs | 1 + tests/expectations/enum_self_flags.c | 16 ++++----- tests/expectations/enum_self_flags.compat.c | 16 ++++----- tests/expectations/enum_self_flags.pyx | 16 ++++----- 9 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index 981da85fe..dbd0b7d64 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -13,7 +13,8 @@ use std::rc::Rc; use crate::bindgen::config::{Config, Language}; use crate::bindgen::ir::{ - Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, Typedef, + Constant, Enum, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Type, + Typedef, }; use crate::bindgen::language_backend::{ CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, @@ -26,6 +27,9 @@ pub struct Bindings { /// The map from path to struct, used to lookup whether a given type is a /// transparent struct. This is needed to generate code for constants. struct_map: ItemMap, + /// The map from path to enum, used to validate enum-variant paths that + /// appear in constant literals. + enum_map: ItemMap, typedef_map: ItemMap, struct_fileds_memo: RefCell>>>, pub globals: Vec, @@ -44,6 +48,7 @@ impl Bindings { pub(crate) fn new( config: Config, struct_map: ItemMap, + enum_map: ItemMap, typedef_map: ItemMap, constants: Vec, globals: Vec, @@ -56,6 +61,7 @@ impl Bindings { Bindings { config, struct_map, + enum_map, typedef_map, struct_fileds_memo: Default::default(), globals, @@ -100,6 +106,37 @@ impl Bindings { any } + pub fn enum_exists(&self, path: &BindgenPath) -> bool { + let mut any = false; + self.enum_map.for_items(path, |_| any = true); + any + } + + /// Returns how the variant `variant_name` of the enum at `path` should be + /// referenced in generated code: `Enum::Variant` for a C++ `enum class`, + /// or the bare (possibly prefixed) variant name otherwise. Returns `None` + /// if the enum or the variant isn't known. + pub fn enum_variant_reference(&self, path: &BindgenPath, variant_name: &str) -> Option { + let config = &self.config; + let mut result = None; + self.enum_map.for_items(path, |e| { + if result.is_some() { + return; + } + let Some(variant) = e.variants.iter().find(|v| v.name == variant_name) else { + return; + }; + let qualify = + config.language == Language::Cxx && config.enumeration.enum_class(&e.annotations); + result = Some(if qualify { + format!("{}::{}", e.export_name, variant.export_name) + } else { + variant.export_name.clone() + }); + }); + result + } + pub fn struct_field_names(&self, path: &BindgenPath) -> Rc> { let mut memos = self.struct_fileds_memo.borrow_mut(); if let Some(memo) = memos.get(path) { diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index b206ced9d..74aeef1bc 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -365,6 +365,7 @@ impl Builder { Default::default(), Default::default(), Default::default(), + Default::default(), true, String::new(), )); diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 918daaed4..d9723b68b 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -195,6 +195,7 @@ impl Literal { } => { if let Some((ref path, _export_name)) = associated_to { return bindings.struct_exists(path) + || bindings.enum_exists(path) || to_known_assoc_constant(path, name).is_some(); } true diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index dba5a10ac..1bd65927f 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -848,6 +848,9 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { if let Some(known) = to_known_assoc_constant(path, name) { return write!(out, "{known}"); } + if let Some(variant) = out.bindings().enum_variant_reference(path, name) { + return write!(out, "{variant}"); + } let path_separator = if self.config.language == Language::C { "_" } else if self.config.structure.associated_constants_in_body { diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index b6b2ba376..3490e6300 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -348,6 +348,9 @@ impl LanguageBackend for CythonLanguageBackend<'_> { if let Some(known) = to_known_assoc_constant(path, name) { return write!(out, "{known}"); } + if let Some(variant) = out.bindings().enum_variant_reference(path, name) { + return write!(out, "{variant}"); + } write!(out, "{export_name}_") } write!(out, "{name}") diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 671f5d9b0..9bccceb59 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -137,6 +137,7 @@ impl Library { Ok(Bindings::new( self.config, self.structs, + self.enums, self.typedefs, constants, globals, diff --git a/tests/expectations/enum_self_flags.c b/tests/expectations/enum_self_flags.c index 81c717c06..47d417d7c 100644 --- a/tests/expectations/enum_self_flags.c +++ b/tests/expectations/enum_self_flags.c @@ -93,14 +93,14 @@ enum PositionAreaKeyword #endif // __STDC_VERSION__ >= 202311L { None = 0, - Center = (uint8_t)PositionAreaTrack_Center, - SpanAll = (uint8_t)PositionAreaTrack_SpanAll, - Start = (uint8_t)PositionAreaTrack_Start, - End = (uint8_t)PositionAreaTrack_End, - SpanStart = (uint8_t)PositionAreaTrack_SpanStart, - SpanEnd = (uint8_t)PositionAreaTrack_SpanEnd, - Top = (((uint8_t)PositionAreaAxis_Vertical << AXIS_SHIFT) | (uint8_t)PositionAreaTrack_Start), - Bottom = (((uint8_t)PositionAreaAxis_Vertical << AXIS_SHIFT) | (uint8_t)PositionAreaTrack_End), + Center = (uint8_t)Center, + SpanAll = (uint8_t)SpanAll, + Start = (uint8_t)Start, + End = (uint8_t)End, + SpanStart = (uint8_t)SpanStart, + SpanEnd = (uint8_t)SpanEnd, + Top = (((uint8_t)Vertical << AXIS_SHIFT) | (uint8_t)Start), + Bottom = (((uint8_t)Vertical << AXIS_SHIFT) | (uint8_t)End), }; #if __STDC_VERSION__ >= 202311L typedef enum PositionAreaKeyword PositionAreaKeyword; diff --git a/tests/expectations/enum_self_flags.compat.c b/tests/expectations/enum_self_flags.compat.c index 5d3737101..c7afb9646 100644 --- a/tests/expectations/enum_self_flags.compat.c +++ b/tests/expectations/enum_self_flags.compat.c @@ -97,14 +97,14 @@ enum PositionAreaKeyword #endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L { None = 0, - Center = (uint8_t)PositionAreaTrack_Center, - SpanAll = (uint8_t)PositionAreaTrack_SpanAll, - Start = (uint8_t)PositionAreaTrack_Start, - End = (uint8_t)PositionAreaTrack_End, - SpanStart = (uint8_t)PositionAreaTrack_SpanStart, - SpanEnd = (uint8_t)PositionAreaTrack_SpanEnd, - Top = (((uint8_t)PositionAreaAxis_Vertical << AXIS_SHIFT) | (uint8_t)PositionAreaTrack_Start), - Bottom = (((uint8_t)PositionAreaAxis_Vertical << AXIS_SHIFT) | (uint8_t)PositionAreaTrack_End), + Center = (uint8_t)Center, + SpanAll = (uint8_t)SpanAll, + Start = (uint8_t)Start, + End = (uint8_t)End, + SpanStart = (uint8_t)SpanStart, + SpanEnd = (uint8_t)SpanEnd, + Top = (((uint8_t)Vertical << AXIS_SHIFT) | (uint8_t)Start), + Bottom = (((uint8_t)Vertical << AXIS_SHIFT) | (uint8_t)End), }; #ifndef __cplusplus #if __STDC_VERSION__ >= 202311L diff --git a/tests/expectations/enum_self_flags.pyx b/tests/expectations/enum_self_flags.pyx index 0f65b97bc..6ca37c00e 100644 --- a/tests/expectations/enum_self_flags.pyx +++ b/tests/expectations/enum_self_flags.pyx @@ -56,14 +56,14 @@ cdef extern from *: # https://drafts.csswg.org/css-anchor-position-1/#propdef-position-area cdef enum: None # = 0, - Center # = PositionAreaTrack_Center, - SpanAll # = PositionAreaTrack_SpanAll, - Start # = PositionAreaTrack_Start, - End # = PositionAreaTrack_End, - SpanStart # = PositionAreaTrack_SpanStart, - SpanEnd # = PositionAreaTrack_SpanEnd, - Top # = ((PositionAreaAxis_Vertical << AXIS_SHIFT) | PositionAreaTrack_Start), - Bottom # = ((PositionAreaAxis_Vertical << AXIS_SHIFT) | PositionAreaTrack_End), + Center # = Center, + SpanAll # = SpanAll, + Start # = Start, + End # = End, + SpanStart # = SpanStart, + SpanEnd # = SpanEnd, + Top # = ((Vertical << AXIS_SHIFT) | Start), + Bottom # = ((Vertical << AXIS_SHIFT) | End), ctypedef uint8_t PositionAreaKeyword; void root(PositionAreaKeyword, PositionAreaTrack, PositionAreaAxis); From 5ba4600269f89076f070ee07dba338091dc42f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Jun 2026 19:11:46 +0200 Subject: [PATCH 3/5] ir: Add support for arrays. --- src/bindgen/ir/constant.rs | 49 ++++++++++++++++++++++---- src/bindgen/language_backend/clike.rs | 8 +++++ src/bindgen/language_backend/cython.rs | 8 +++++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index d9723b68b..d55151cfb 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -114,6 +114,9 @@ pub enum Literal { ty: Type, value: Box, }, + Array { + items: Vec, + }, } impl Literal { @@ -182,6 +185,11 @@ impl Literal { } } } + Literal::Array { ref mut items } => { + for item in items { + item.replace_self_with(self_ty); + } + } Literal::Expr(..) => {} } } @@ -189,6 +197,7 @@ impl Literal { fn is_valid(&self, bindings: &Bindings) -> bool { match *self { Literal::Expr(..) => true, + Literal::Array { ref items } => items.iter().all(|i| i.is_valid(bindings)), Literal::Path { ref associated_to, ref name, @@ -228,6 +237,14 @@ impl Literal { ref right, .. } => left.visit(visitor) && right.visit(visitor), + Literal::Array { ref items } => { + for item in items { + if !item.visit(visitor) { + return false; + } + } + true + } Literal::FieldAccess { ref base, .. } => base.visit(visitor), Literal::Struct { ref fields, .. } => { for (_name, field) in fields.iter() { @@ -305,6 +322,11 @@ impl Literal { left.rename_for_config(config); right.rename_for_config(config); } + Literal::Array { ref mut items } => { + for item in items { + item.rename_for_config(config); + } + } Literal::Expr(_) => {} Literal::Cast { ref mut ty, @@ -506,6 +528,14 @@ impl Literal { } } + syn::Expr::Array(syn::ExprArray { ref elems, .. }) => { + let mut items = vec![]; + for elem in elems { + items.push(Self::load(elem)?); + } + Ok(Literal::Array { items }) + } + _ => Err(format!("Unsupported expression. {:?}", *expr)), } } @@ -665,8 +695,14 @@ impl Constant { } else { out.write("static const "); } - language_backend.write_type(out, &self.ty); - write!(out, " {};", self.export_name()); + crate::bindgen::cdecl::write_field( + language_backend, + out, + &self.ty, + self.export_name(), + config, + ); + write!(out, ";"); condition.write_after(config, out); } @@ -759,9 +795,8 @@ impl Constant { } else { out.write("const "); } - - language_backend.write_type(out, &self.ty); - write!(out, " {name} = "); + crate::bindgen::cdecl::write_field(language_backend, out, &self.ty, &name, config); + write!(out, " = "); language_backend.write_literal(out, value); write!(out, ";"); } @@ -771,10 +806,10 @@ impl Constant { } Language::Cython => { out.write("const "); - language_backend.write_type(out, &self.ty); // For extern Cython declarations the initializer is ignored, // but still useful as documentation, so we write it as a comment. - write!(out, " {name} # = "); + crate::bindgen::cdecl::write_field(language_backend, out, &self.ty, &name, config); + write!(out, " # = "); language_backend.write_literal(out, value); } } diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index 1bd65927f..4d927abe2 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -840,6 +840,14 @@ impl LanguageBackend for CLikeLanguageBackend<'_> { fn write_literal(&mut self, out: &mut SourceWriter, l: &Literal) { match l { Literal::Expr(v) => write!(out, "{v}"), + Literal::Array { items } => { + write!(out, "{{ "); + for item in items { + self.write_literal(out, item); + write!(out, ", "); + } + write!(out, "}}"); + } Literal::Path { ref associated_to, ref name, diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs index 3490e6300..9a95bc9ab 100644 --- a/src/bindgen/language_backend/cython.rs +++ b/src/bindgen/language_backend/cython.rs @@ -384,6 +384,14 @@ impl LanguageBackend for CythonLanguageBackend<'_> { out.write(">"); self.write_literal(out, value); } + Literal::Array { ref items } => { + write!(out, "[ "); + for item in items { + self.write_literal(out, item); + write!(out, ", "); + } + write!(out, "]"); + } Literal::Struct { export_name, fields, From 2b4af1f8b7d31bfa127d3c92bf41ff8e415daab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Jun 2026 19:38:50 +0200 Subject: [PATCH 4/5] tests: Add some tests for constant enums. --- tests/expectations-symbols/const_enum.c.sym | 3 ++ tests/expectations/const_enum.c | 30 ++++++++++++++++ tests/expectations/const_enum.compat.c | 40 +++++++++++++++++++++ tests/expectations/const_enum.cpp | 26 ++++++++++++++ tests/expectations/const_enum.pyx | 23 ++++++++++++ tests/expectations/const_enum_both.c | 30 ++++++++++++++++ tests/expectations/const_enum_both.compat.c | 40 +++++++++++++++++++++ tests/expectations/const_enum_tag.c | 30 ++++++++++++++++ tests/expectations/const_enum_tag.compat.c | 40 +++++++++++++++++++++ tests/expectations/const_enum_tag.pyx | 23 ++++++++++++ tests/rust/const_enum.rs | 22 ++++++++++++ 11 files changed, 307 insertions(+) create mode 100644 tests/expectations-symbols/const_enum.c.sym create mode 100644 tests/expectations/const_enum.c create mode 100644 tests/expectations/const_enum.compat.c create mode 100644 tests/expectations/const_enum.cpp create mode 100644 tests/expectations/const_enum.pyx create mode 100644 tests/expectations/const_enum_both.c create mode 100644 tests/expectations/const_enum_both.compat.c create mode 100644 tests/expectations/const_enum_tag.c create mode 100644 tests/expectations/const_enum_tag.compat.c create mode 100644 tests/expectations/const_enum_tag.pyx create mode 100644 tests/rust/const_enum.rs diff --git a/tests/expectations-symbols/const_enum.c.sym b/tests/expectations-symbols/const_enum.c.sym new file mode 100644 index 000000000..f018156fc --- /dev/null +++ b/tests/expectations-symbols/const_enum.c.sym @@ -0,0 +1,3 @@ +{ +root; +}; \ No newline at end of file diff --git a/tests/expectations/const_enum.c b/tests/expectations/const_enum.c new file mode 100644 index 000000000..c7632f5d3 --- /dev/null +++ b/tests/expectations/const_enum.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +enum FillRule +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L + +typedef struct { + FillRule rule; +} Style; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +void root(FillRule rule, Style style); diff --git a/tests/expectations/const_enum.compat.c b/tests/expectations/const_enum.compat.c new file mode 100644 index 000000000..c7be3ca45 --- /dev/null +++ b/tests/expectations/const_enum.compat.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +enum FillRule +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +typedef struct { + FillRule rule; +} Style; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(FillRule rule, Style style); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_enum.cpp b/tests/expectations/const_enum.cpp new file mode 100644 index 000000000..f7544adc7 --- /dev/null +++ b/tests/expectations/const_enum.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include + +enum class FillRule : uint8_t { + Nonzero, + Evenodd, +}; + +struct Style { + FillRule rule; +}; +constexpr static const FillRule Style_DEFAULT_RULE = FillRule::Nonzero; +constexpr static const FillRule Style_ALL_RULES[2] = { FillRule::Nonzero, FillRule::Evenodd, }; + +constexpr static const FillRule DEFAULT_FILL_RULE = FillRule::Nonzero; + +constexpr static const FillRule ALL_FILL_RULES[2] = { FillRule::Nonzero, FillRule::Evenodd, }; + +extern "C" { + +void root(FillRule rule, Style style); + +} // extern "C" diff --git a/tests/expectations/const_enum.pyx b/tests/expectations/const_enum.pyx new file mode 100644 index 000000000..01bbcd898 --- /dev/null +++ b/tests/expectations/const_enum.pyx @@ -0,0 +1,23 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef enum: + Nonzero, + Evenodd, + ctypedef uint8_t FillRule; + + ctypedef struct Style: + FillRule rule; + const FillRule Style_DEFAULT_RULE # = Nonzero + const FillRule Style_ALL_RULES[2] # = [ Nonzero, Evenodd, ] + + const FillRule DEFAULT_FILL_RULE # = Nonzero + + const FillRule ALL_FILL_RULES[2] # = [ Nonzero, Evenodd, ] + + void root(FillRule rule, Style style); diff --git a/tests/expectations/const_enum_both.c b/tests/expectations/const_enum_both.c new file mode 100644 index 000000000..dc6026f44 --- /dev/null +++ b/tests/expectations/const_enum_both.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +enum FillRule +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L + +typedef struct Style { + FillRule rule; +} Style; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +void root(FillRule rule, struct Style style); diff --git a/tests/expectations/const_enum_both.compat.c b/tests/expectations/const_enum_both.compat.c new file mode 100644 index 000000000..3e1605530 --- /dev/null +++ b/tests/expectations/const_enum_both.compat.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +enum FillRule +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +typedef struct Style { + FillRule rule; +} Style; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(FillRule rule, struct Style style); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_enum_tag.c b/tests/expectations/const_enum_tag.c new file mode 100644 index 000000000..cf5e44bd8 --- /dev/null +++ b/tests/expectations/const_enum_tag.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +enum FillRule +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L + +struct Style { + FillRule rule; +}; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +void root(FillRule rule, struct Style style); diff --git a/tests/expectations/const_enum_tag.compat.c b/tests/expectations/const_enum_tag.compat.c new file mode 100644 index 000000000..f3f2e1216 --- /dev/null +++ b/tests/expectations/const_enum_tag.compat.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include + +enum FillRule +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + Nonzero, + Evenodd, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum FillRule FillRule; +#else +typedef uint8_t FillRule; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +struct Style { + FillRule rule; +}; +#define Style_DEFAULT_RULE Nonzero +#define Style_ALL_RULES { Nonzero, Evenodd, } + +#define DEFAULT_FILL_RULE Nonzero + +#define ALL_FILL_RULES { Nonzero, Evenodd, } + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(FillRule rule, struct Style style); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/const_enum_tag.pyx b/tests/expectations/const_enum_tag.pyx new file mode 100644 index 000000000..fe7127b4a --- /dev/null +++ b/tests/expectations/const_enum_tag.pyx @@ -0,0 +1,23 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef enum: + Nonzero, + Evenodd, + ctypedef uint8_t FillRule; + + cdef struct Style: + FillRule rule; + const FillRule Style_DEFAULT_RULE # = Nonzero + const FillRule Style_ALL_RULES[2] # = [ Nonzero, Evenodd, ] + + const FillRule DEFAULT_FILL_RULE # = Nonzero + + const FillRule ALL_FILL_RULES[2] # = [ Nonzero, Evenodd, ] + + void root(FillRule rule, Style style); diff --git a/tests/rust/const_enum.rs b/tests/rust/const_enum.rs new file mode 100644 index 000000000..255486237 --- /dev/null +++ b/tests/rust/const_enum.rs @@ -0,0 +1,22 @@ +#[repr(u8)] +pub enum FillRule { + Nonzero, + Evenodd, +} + +pub const DEFAULT_FILL_RULE: FillRule = FillRule::Nonzero; + +pub const ALL_FILL_RULES: [FillRule; 2] = [FillRule::Nonzero, FillRule::Evenodd]; + +#[repr(C)] +pub struct Style { + rule: FillRule, +} + +impl Style { + pub const DEFAULT_RULE: FillRule = FillRule::Nonzero; + pub const ALL_RULES: [FillRule; 2] = [FillRule::Nonzero, FillRule::Evenodd]; +} + +#[no_mangle] +pub extern "C" fn root(rule: FillRule, style: Style) {} From 1bf22a8e65e61989cd0b5832c0f6857f24cdd9bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 9 Jun 2026 20:41:32 +0200 Subject: [PATCH 5/5] Bump version. --- CHANGES | 4 ++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 569dcac40..a8064a8ca 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ # unreleased +# 0.29.4 + + * Support constant enums and arrays. + # 0.29.3 * Expose the line_endings config option to use with the builder diff --git a/Cargo.lock b/Cargo.lock index 5f2803814..b1d6310cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,7 +66,7 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cbindgen" -version = "0.29.3" +version = "0.29.4" dependencies = [ "clap", "heck", diff --git a/Cargo.toml b/Cargo.toml index 2d0ac3a1d..457875b02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cbindgen" -version = "0.29.3" +version = "0.29.4" authors = [ "Emilio Cobos Álvarez ", "Jeff Muizelaar ",