From 3fbebee466ce70a37bca66e26eb785976aaa538b Mon Sep 17 00:00:00 2001 From: Don Yin Date: Mon, 26 Oct 2026 16:06:58 +0000 Subject: [PATCH 1/2] Guard cfg'd fields in generated C++ operator== and operator!= Fixes #937 Signed-off-by: Don Yin --- src/bindgen/language_backend/clike.rs | 43 +++++++++++++++++++-------- tests/expectations/cfg.cpp | 15 ++++++++++ tests/rust/cfg.rs | 3 -- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs index 4d927abe2..c105bc54d 100644 --- a/src/bindgen/language_backend/clike.rs +++ b/src/bindgen/language_backend/clike.rs @@ -287,19 +287,36 @@ impl<'a> CLikeLanguageBackend<'a> { other ); out.open_brace(); - out.write("return "); - let vec: Vec<_> = s - .fields - .iter() - .skip(skip_fields) - .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) - .collect(); - out.write_vertical_source_list( - self, - &vec[..], - ListType::Join(&format!(" {}", $conjuc)), - |_, out, s| write!(out, "{}", s), - ); + out.write("return"); + let fields = &s.fields[skip_fields..]; + if matches!($op, "==" | "!=") && fields.iter().any(|field| field.cfg.is_some()) { + out.new_line(); + for field in fields { + let condition = field.cfg.to_condition(self.config); + condition.write_before(self.config, out); + write!( + out, + "{} {} {}.{} {}", + field.name, $op, other, field.name, $conjuc + ); + condition.write_after(self.config, out); + out.new_line(); + } + // A neutral operand keeps the expression valid even if all fields are absent. + write!(out, "{}", $op == "=="); + } else { + out.write(" "); + let vec: Vec<_> = fields + .iter() + .map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name)) + .collect(); + out.write_vertical_source_list( + self, + &vec[..], + ListType::Join(&format!(" {}", $conjuc)), + |_, out, s| write!(out, "{}", s), + ); + } out.write(";"); out.close_brace(false); }}; diff --git a/tests/expectations/cfg.cpp b/tests/expectations/cfg.cpp index 13063484b..052876311 100644 --- a/tests/expectations/cfg.cpp +++ b/tests/expectations/cfg.cpp @@ -266,6 +266,21 @@ struct ConditionalField { int32_t field #endif ; + + bool operator==(const ConditionalField& other) const { + return +#if defined(X11) + field == other.field && +#endif + true; + } + bool operator!=(const ConditionalField& other) const { + return +#if defined(X11) + field != other.field || +#endif + false; + } }; constexpr static const ConditionalField ConditionalField_ZERO = ConditionalField{ #if defined(X11) diff --git a/tests/rust/cfg.rs b/tests/rust/cfg.rs index b42c1d1a7..a002333d2 100644 --- a/tests/rust/cfg.rs +++ b/tests/rust/cfg.rs @@ -60,9 +60,6 @@ struct BarHandle { y: f32, } -// FIXME(#634): Support deriving methods for structs with conditional fields. -/// cbindgen:derive-eq=false -/// cbindgen:derive-neq=false #[repr(C)] struct ConditionalField { #[cfg(x11)] From 002c66a61ec7fcbdd9a458de536df1f21beb1c93 Mon Sep 17 00:00:00 2001 From: Don Yin Date: Mon, 26 Oct 2026 16:08:01 +0000 Subject: [PATCH 2/2] Add a CHANGES entry Signed-off-by: Don Yin --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 6fd28678c..f34ae9eda 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,8 @@ # unreleased + * Wrap conditional fields in `#if` blocks in generated C++ `operator==` and + `operator!=`, so headers still compile when a feature is disabled (#937). + * Emit `&str`, `&CStr`, and arrays thereof as C string literals, and byte-string (`b"..."`) constants as `uint8_t[]` arrays, instead of dropping them.