From 7bcbf2f98936b9acac1b0f2e1e79744d20ea2921 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 4 Jul 2026 13:10:10 +0200 Subject: [PATCH] Value-initialize the result local in generated C++ tagged-enum constructors Emit `T result{};` instead of `T result;` in the generated static variant constructors so the payload union is zero-initialized rather than left indeterminate. For no-payload variants only the tag is set, so the previously-uninitialized union produced false "uninitialized scalar" reports from static-analysis tools (e.g. Coverity). Payload variants are unaffected: the union is value-initialized and the payload is then constructed in place via placement new as before. Add a regression test (tagged_enum_value_init) covering no-payload and payload variants, and regenerate the affected C++ expectations. --- src/bindgen/ir/enumeration.rs | 2 +- .../tagged_enum_value_init.c.sym | 3 + tests/expectations/annotation.cpp | 12 +-- tests/expectations/array.cpp | 2 +- tests/expectations/asserted_cast.cpp | 18 ++--- tests/expectations/cfg.cpp | 8 +- .../expectations/destructor_and_copy_ctor.cpp | 46 +++++------ tests/expectations/tagged_enum_value_init.c | 50 ++++++++++++ .../tagged_enum_value_init.compat.c | 60 +++++++++++++++ tests/expectations/tagged_enum_value_init.cpp | 76 +++++++++++++++++++ tests/expectations/tagged_enum_value_init.pyx | 32 ++++++++ .../tagged_enum_value_init_both.c | 50 ++++++++++++ .../tagged_enum_value_init_both.compat.c | 60 +++++++++++++++ .../expectations/tagged_enum_value_init_tag.c | 50 ++++++++++++ .../tagged_enum_value_init_tag.compat.c | 60 +++++++++++++++ .../tagged_enum_value_init_tag.pyx | 32 ++++++++ tests/expectations/transform_op.cpp | 28 +++---- tests/rust/tagged_enum_value_init.rs | 18 +++++ 18 files changed, 549 insertions(+), 58 deletions(-) create mode 100644 tests/expectations-symbols/tagged_enum_value_init.c.sym create mode 100644 tests/expectations/tagged_enum_value_init.c create mode 100644 tests/expectations/tagged_enum_value_init.compat.c create mode 100644 tests/expectations/tagged_enum_value_init.cpp create mode 100644 tests/expectations/tagged_enum_value_init.pyx create mode 100644 tests/expectations/tagged_enum_value_init_both.c create mode 100644 tests/expectations/tagged_enum_value_init_both.compat.c create mode 100644 tests/expectations/tagged_enum_value_init_tag.c create mode 100644 tests/expectations/tagged_enum_value_init_tag.compat.c create mode 100644 tests/expectations/tagged_enum_value_init_tag.pyx create mode 100644 tests/rust/tagged_enum_value_init.rs diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index aa83a7802..203fa6fba 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -1204,7 +1204,7 @@ impl Enum { write!(out, ")"); out.open_brace(); - write!(out, "{} result;", self.export_name); + write!(out, "{} result{{}};", self.export_name); if let VariantBody::Body { name: ref variant_name, diff --git a/tests/expectations-symbols/tagged_enum_value_init.c.sym b/tests/expectations-symbols/tagged_enum_value_init.c.sym new file mode 100644 index 000000000..f018156fc --- /dev/null +++ b/tests/expectations-symbols/tagged_enum_value_init.c.sym @@ -0,0 +1,3 @@ +{ +root; +}; \ No newline at end of file diff --git a/tests/expectations/annotation.cpp b/tests/expectations/annotation.cpp index 1197f4501..c0abd665e 100644 --- a/tests/expectations/annotation.cpp +++ b/tests/expectations/annotation.cpp @@ -54,7 +54,7 @@ union F { Bar_Body bar; static F Foo(const int16_t &_0) { - F result; + F result{}; ::new (&result.foo._0) (int16_t)(_0); result.tag = Tag::Foo; return result; @@ -66,7 +66,7 @@ union F { static F Bar(const uint8_t &x, const int16_t &y) { - F result; + F result{}; ::new (&result.bar.x) (uint8_t)(x); ::new (&result.bar.y) (int16_t)(y); result.tag = Tag::Bar; @@ -78,7 +78,7 @@ union F { } static F Baz() { - F result; + F result{}; result.tag = Tag::Baz; return result; } @@ -111,7 +111,7 @@ struct H { }; static H Hello(const int16_t &_0) { - H result; + H result{}; ::new (&result.hello._0) (int16_t)(_0); result.tag = Tag::Hello; return result; @@ -123,7 +123,7 @@ struct H { static H There(const uint8_t &x, const int16_t &y) { - H result; + H result{}; ::new (&result.there.x) (uint8_t)(x); ::new (&result.there.y) (int16_t)(y); result.tag = Tag::There; @@ -135,7 +135,7 @@ struct H { } static H Everyone() { - H result; + H result{}; result.tag = Tag::Everyone; return result; } diff --git a/tests/expectations/array.cpp b/tests/expectations/array.cpp index 7e34fe9ca..12f3b48ea 100644 --- a/tests/expectations/array.cpp +++ b/tests/expectations/array.cpp @@ -19,7 +19,7 @@ struct Foo { }; static Foo A(const float (&_0)[20]) { - Foo result; + Foo result{}; for (int i = 0; i < 20; i++) { ::new (&result.a._0[i]) (float)(_0[i]); } diff --git a/tests/expectations/asserted_cast.cpp b/tests/expectations/asserted_cast.cpp index 3dfe52041..baba337db 100644 --- a/tests/expectations/asserted_cast.cpp +++ b/tests/expectations/asserted_cast.cpp @@ -33,7 +33,7 @@ struct H { }; static H H_Foo(const int16_t &_0) { - H result; + H result{}; ::new (&result.foo._0) (int16_t)(_0); result.tag = Tag::H_Foo; return result; @@ -55,7 +55,7 @@ struct H { static H H_Bar(const uint8_t &x, const int16_t &y) { - H result; + H result{}; ::new (&result.bar.x) (uint8_t)(x); ::new (&result.bar.y) (int16_t)(y); result.tag = Tag::H_Bar; @@ -77,7 +77,7 @@ struct H { } static H H_Baz() { - H result; + H result{}; result.tag = Tag::H_Baz; return result; } @@ -110,7 +110,7 @@ struct J { }; static J J_Foo(const int16_t &_0) { - J result; + J result{}; ::new (&result.foo._0) (int16_t)(_0); result.tag = Tag::J_Foo; return result; @@ -132,7 +132,7 @@ struct J { static J J_Bar(const uint8_t &x, const int16_t &y) { - J result; + J result{}; ::new (&result.bar.x) (uint8_t)(x); ::new (&result.bar.y) (int16_t)(y); result.tag = Tag::J_Bar; @@ -154,7 +154,7 @@ struct J { } static J J_Baz() { - J result; + J result{}; result.tag = Tag::J_Baz; return result; } @@ -189,7 +189,7 @@ union K { K_Bar_Body bar; static K K_Foo(const int16_t &_0) { - K result; + K result{}; ::new (&result.foo._0) (int16_t)(_0); result.tag = Tag::K_Foo; return result; @@ -211,7 +211,7 @@ union K { static K K_Bar(const uint8_t &x, const int16_t &y) { - K result; + K result{}; ::new (&result.bar.x) (uint8_t)(x); ::new (&result.bar.y) (int16_t)(y); result.tag = Tag::K_Bar; @@ -233,7 +233,7 @@ union K { } static K K_Baz() { - K result; + K result{}; result.tag = Tag::K_Baz; return result; } diff --git a/tests/expectations/cfg.cpp b/tests/expectations/cfg.cpp index 13063484b..0e4cb4109 100644 --- a/tests/expectations/cfg.cpp +++ b/tests/expectations/cfg.cpp @@ -147,7 +147,7 @@ union C { #endif static C C1() { - C result; + C result{}; result.tag = Tag::C1; return result; } @@ -157,7 +157,7 @@ union C { } static C C2() { - C result; + C result{}; result.tag = Tag::C2; return result; } @@ -168,7 +168,7 @@ union C { #if defined(PLATFORM_WIN) static C C3() { - C result; + C result{}; result.tag = Tag::C3; return result; } @@ -180,7 +180,7 @@ union C { #if defined(PLATFORM_UNIX) static C C5(const int32_t &int_) { - C result; + C result{}; ::new (&result.c5.int_) (int32_t)(int_); result.tag = Tag::C5; return result; diff --git a/tests/expectations/destructor_and_copy_ctor.cpp b/tests/expectations/destructor_and_copy_ctor.cpp index 005b0863e..3d2f28d8f 100644 --- a/tests/expectations/destructor_and_copy_ctor.cpp +++ b/tests/expectations/destructor_and_copy_ctor.cpp @@ -71,7 +71,7 @@ struct Foo { }; static Foo Bar() { - Foo result; + Foo result{}; result.tag = Tag::Bar; return result; } @@ -81,7 +81,7 @@ struct Foo { } static Foo Polygon1(const Polygon &_0) { - Foo result; + Foo result{}; ::new (&result.polygon1._0) (Polygon)(_0); result.tag = Tag::Polygon1; return result; @@ -92,7 +92,7 @@ struct Foo { } static Foo Slice1(const OwnedSlice &_0) { - Foo result; + Foo result{}; ::new (&result.slice1._0) (OwnedSlice)(_0); result.tag = Tag::Slice1; return result; @@ -103,7 +103,7 @@ struct Foo { } static Foo Slice2(const OwnedSlice &_0) { - Foo result; + Foo result{}; ::new (&result.slice2._0) (OwnedSlice)(_0); result.tag = Tag::Slice2; return result; @@ -115,7 +115,7 @@ struct Foo { static Foo Slice3(const FillRule &fill, const OwnedSlice &coords) { - Foo result; + Foo result{}; ::new (&result.slice3.fill) (FillRule)(fill); ::new (&result.slice3.coords) (OwnedSlice)(coords); result.tag = Tag::Slice3; @@ -128,7 +128,7 @@ struct Foo { static Foo Slice4(const FillRule &fill, const OwnedSlice &coords) { - Foo result; + Foo result{}; ::new (&result.slice4.fill) (FillRule)(fill); ::new (&result.slice4.coords) (OwnedSlice)(coords); result.tag = Tag::Slice4; @@ -225,7 +225,7 @@ union Baz { Slice24_Body slice24; static Baz Bar2() { - Baz result; + Baz result{}; result.tag = Tag::Bar2; return result; } @@ -235,7 +235,7 @@ union Baz { } static Baz Polygon21(const Polygon &_0) { - Baz result; + Baz result{}; ::new (&result.polygon21._0) (Polygon)(_0); result.tag = Tag::Polygon21; return result; @@ -246,7 +246,7 @@ union Baz { } static Baz Slice21(const OwnedSlice &_0) { - Baz result; + Baz result{}; ::new (&result.slice21._0) (OwnedSlice)(_0); result.tag = Tag::Slice21; return result; @@ -257,7 +257,7 @@ union Baz { } static Baz Slice22(const OwnedSlice &_0) { - Baz result; + Baz result{}; ::new (&result.slice22._0) (OwnedSlice)(_0); result.tag = Tag::Slice22; return result; @@ -269,7 +269,7 @@ union Baz { static Baz Slice23(const FillRule &fill, const OwnedSlice &coords) { - Baz result; + Baz result{}; ::new (&result.slice23.fill) (FillRule)(fill); ::new (&result.slice23.coords) (OwnedSlice)(coords); result.tag = Tag::Slice23; @@ -282,7 +282,7 @@ union Baz { static Baz Slice24(const FillRule &fill, const OwnedSlice &coords) { - Baz result; + Baz result{}; ::new (&result.slice24.fill) (FillRule)(fill); ::new (&result.slice24.coords) (OwnedSlice)(coords); result.tag = Tag::Slice24; @@ -355,7 +355,7 @@ union Taz { Taz3_Body taz3; static Taz Bar3() { - Taz result; + Taz result{}; result.tag = Tag::Bar3; return result; } @@ -365,7 +365,7 @@ union Taz { } static Taz Taz1(const int32_t &_0) { - Taz result; + Taz result{}; ::new (&result.taz1._0) (int32_t)(_0); result.tag = Tag::Taz1; return result; @@ -376,7 +376,7 @@ union Taz { } static Taz Taz3(const OwnedSlice &_0) { - Taz result; + Taz result{}; ::new (&result.taz3._0) (OwnedSlice)(_0); result.tag = Tag::Taz3; return result; @@ -435,7 +435,7 @@ union Tazz { Taz2_Body taz2; static Tazz Bar4() { - Tazz result; + Tazz result{}; result.tag = Tag::Bar4; return result; } @@ -445,7 +445,7 @@ union Tazz { } static Tazz Taz2(const int32_t &_0) { - Tazz result; + Tazz result{}; ::new (&result.taz2._0) (int32_t)(_0); result.tag = Tag::Taz2; return result; @@ -480,7 +480,7 @@ union Tazzz { Taz5_Body taz5; static Tazzz Bar5() { - Tazzz result; + Tazzz result{}; result.tag = Tag::Bar5; return result; } @@ -490,7 +490,7 @@ union Tazzz { } static Tazzz Taz5(const int32_t &_0) { - Tazzz result; + Tazzz result{}; ::new (&result.taz5._0) (int32_t)(_0); result.tag = Tag::Taz5; return result; @@ -546,7 +546,7 @@ union Tazzzz { Taz7_Body taz7; static Tazzzz Taz6(const int32_t &_0) { - Tazzzz result; + Tazzzz result{}; ::new (&result.taz6._0) (int32_t)(_0); result.tag = Tag::Taz6; return result; @@ -557,7 +557,7 @@ union Tazzzz { } static Tazzzz Taz7(const uint32_t &_0) { - Tazzzz result; + Tazzzz result{}; ::new (&result.taz7._0) (uint32_t)(_0); result.tag = Tag::Taz7; return result; @@ -630,7 +630,7 @@ union Qux { Qux2_Body qux2; static Qux Qux1(const int32_t &_0) { - Qux result; + Qux result{}; ::new (&result.qux1._0) (int32_t)(_0); result.tag = Tag::Qux1; return result; @@ -641,7 +641,7 @@ union Qux { } static Qux Qux2(const uint32_t &_0) { - Qux result; + Qux result{}; ::new (&result.qux2._0) (uint32_t)(_0); result.tag = Tag::Qux2; return result; diff --git a/tests/expectations/tagged_enum_value_init.c b/tests/expectations/tagged_enum_value_init.c new file mode 100644 index 000000000..bb23b1783 --- /dev/null +++ b/tests/expectations/tagged_enum_value_init.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L + +typedef struct { + int32_t _0; + uint64_t _1; +} Pair_Body; + +typedef struct { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + Pair_Body pair; + }; +} ValueInitEnum; + +void root(ValueInitEnum e); diff --git a/tests/expectations/tagged_enum_value_init.compat.c b/tests/expectations/tagged_enum_value_init.compat.c new file mode 100644 index 000000000..e89ca74aa --- /dev/null +++ b/tests/expectations/tagged_enum_value_init.compat.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +typedef struct { + int32_t _0; + uint64_t _1; +} Pair_Body; + +typedef struct { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + Pair_Body pair; + }; +} ValueInitEnum; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(ValueInitEnum e); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/tagged_enum_value_init.cpp b/tests/expectations/tagged_enum_value_init.cpp new file mode 100644 index 000000000..0ed74440c --- /dev/null +++ b/tests/expectations/tagged_enum_value_init.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +/// Regression test: in C++ output, the generated static variant constructors of a +/// tagged enum must value-initialize their `result` local (`Foo result{};`) so that +/// the payload union is zero-initialized rather than left indeterminate. This avoids +/// false "uninitialized scalar" reports from static analysis for no-payload variants. +/// +struct ValueInitEnum { + enum class Tag : uint8_t { + /// No payload: `result` is returned with only the tag set, so the union must be + /// value-initialized. + Empty, + /// Payload variant: constructed via placement new over the value-initialized union. + Number, + Pair, + }; + + struct Number_Body { + int32_t _0; + }; + + struct Pair_Body { + int32_t _0; + uint64_t _1; + }; + + Tag tag; + union { + Number_Body number; + Pair_Body pair; + }; + + static ValueInitEnum Empty() { + ValueInitEnum result{}; + result.tag = Tag::Empty; + return result; + } + + bool IsEmpty() const { + return tag == Tag::Empty; + } + + static ValueInitEnum Number(const int32_t &_0) { + ValueInitEnum result{}; + ::new (&result.number._0) (int32_t)(_0); + result.tag = Tag::Number; + return result; + } + + bool IsNumber() const { + return tag == Tag::Number; + } + + static ValueInitEnum Pair(const int32_t &_0, + const uint64_t &_1) { + ValueInitEnum result{}; + ::new (&result.pair._0) (int32_t)(_0); + ::new (&result.pair._1) (uint64_t)(_1); + result.tag = Tag::Pair; + return result; + } + + bool IsPair() const { + return tag == Tag::Pair; + } +}; + +extern "C" { + +void root(ValueInitEnum e); + +} // extern "C" diff --git a/tests/expectations/tagged_enum_value_init.pyx b/tests/expectations/tagged_enum_value_init.pyx new file mode 100644 index 000000000..52fb8f5eb --- /dev/null +++ b/tests/expectations/tagged_enum_value_init.pyx @@ -0,0 +1,32 @@ +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 *: + + # Regression test: in C++ output, the generated static variant constructors of a + # tagged enum must value-initialize their `result` local (`Foo result{};`) so that + # the payload union is zero-initialized rather than left indeterminate. This avoids + # false "uninitialized scalar" reports from static analysis for no-payload variants. + # + cdef enum: + # No payload: `result` is returned with only the tag set, so the union must be + # value-initialized. + Empty, + # Payload variant: constructed via placement new over the value-initialized union. + Number, + Pair, + ctypedef uint8_t ValueInitEnum_Tag; + + ctypedef struct Pair_Body: + int32_t _0; + uint64_t _1; + + ctypedef struct ValueInitEnum: + ValueInitEnum_Tag tag; + int32_t number; + Pair_Body pair; + + void root(ValueInitEnum e); diff --git a/tests/expectations/tagged_enum_value_init_both.c b/tests/expectations/tagged_enum_value_init_both.c new file mode 100644 index 000000000..b6e088f73 --- /dev/null +++ b/tests/expectations/tagged_enum_value_init_both.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L + +typedef struct Pair_Body { + int32_t _0; + uint64_t _1; +} Pair_Body; + +typedef struct ValueInitEnum { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + Pair_Body pair; + }; +} ValueInitEnum; + +void root(struct ValueInitEnum e); diff --git a/tests/expectations/tagged_enum_value_init_both.compat.c b/tests/expectations/tagged_enum_value_init_both.compat.c new file mode 100644 index 000000000..1174a2466 --- /dev/null +++ b/tests/expectations/tagged_enum_value_init_both.compat.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +typedef struct Pair_Body { + int32_t _0; + uint64_t _1; +} Pair_Body; + +typedef struct ValueInitEnum { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + Pair_Body pair; + }; +} ValueInitEnum; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct ValueInitEnum e); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/tagged_enum_value_init_tag.c b/tests/expectations/tagged_enum_value_init_tag.c new file mode 100644 index 000000000..285365bcf --- /dev/null +++ b/tests/expectations/tagged_enum_value_init_tag.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if __STDC_VERSION__ >= 202311L + : uint8_t +#endif // __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L + +struct Pair_Body { + int32_t _0; + uint64_t _1; +}; + +struct ValueInitEnum { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + struct Pair_Body pair; + }; +}; + +void root(struct ValueInitEnum e); diff --git a/tests/expectations/tagged_enum_value_init_tag.compat.c b/tests/expectations/tagged_enum_value_init_tag.compat.c new file mode 100644 index 000000000..5b132dbe3 --- /dev/null +++ b/tests/expectations/tagged_enum_value_init_tag.compat.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +/** + * Regression test: in C++ output, the generated static variant constructors of a + * tagged enum must value-initialize their `result` local (`Foo result{};`) so that + * the payload union is zero-initialized rather than left indeterminate. This avoids + * false "uninitialized scalar" reports from static analysis for no-payload variants. + * + */ +enum ValueInitEnum_Tag +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L + : uint8_t +#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L + { + /** + * No payload: `result` is returned with only the tag set, so the union must be + * value-initialized. + */ + Empty, + /** + * Payload variant: constructed via placement new over the value-initialized union. + */ + Number, + Pair, +}; +#ifndef __cplusplus +#if __STDC_VERSION__ >= 202311L +typedef enum ValueInitEnum_Tag ValueInitEnum_Tag; +#else +typedef uint8_t ValueInitEnum_Tag; +#endif // __STDC_VERSION__ >= 202311L +#endif // __cplusplus + +struct Pair_Body { + int32_t _0; + uint64_t _1; +}; + +struct ValueInitEnum { + ValueInitEnum_Tag tag; + union { + struct { + int32_t number; + }; + struct Pair_Body pair; + }; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root(struct ValueInitEnum e); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/tagged_enum_value_init_tag.pyx b/tests/expectations/tagged_enum_value_init_tag.pyx new file mode 100644 index 000000000..81cc4e068 --- /dev/null +++ b/tests/expectations/tagged_enum_value_init_tag.pyx @@ -0,0 +1,32 @@ +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 *: + + # Regression test: in C++ output, the generated static variant constructors of a + # tagged enum must value-initialize their `result` local (`Foo result{};`) so that + # the payload union is zero-initialized rather than left indeterminate. This avoids + # false "uninitialized scalar" reports from static analysis for no-payload variants. + # + cdef enum: + # No payload: `result` is returned with only the tag set, so the union must be + # value-initialized. + Empty, + # Payload variant: constructed via placement new over the value-initialized union. + Number, + Pair, + ctypedef uint8_t ValueInitEnum_Tag; + + cdef struct Pair_Body: + int32_t _0; + uint64_t _1; + + cdef struct ValueInitEnum: + ValueInitEnum_Tag tag; + int32_t number; + Pair_Body pair; + + void root(ValueInitEnum e); diff --git a/tests/expectations/transform_op.cpp b/tests/expectations/transform_op.cpp index 2d070ba48..c58b46d60 100644 --- a/tests/expectations/transform_op.cpp +++ b/tests/expectations/transform_op.cpp @@ -47,7 +47,7 @@ union StyleFoo { static StyleFoo Foo(const int32_t &x, const StylePoint &y, const StylePoint &z) { - StyleFoo result; + StyleFoo result{}; ::new (&result.foo.x) (int32_t)(x); ::new (&result.foo.y) (StylePoint)(y); ::new (&result.foo.z) (StylePoint)(z); @@ -70,7 +70,7 @@ union StyleFoo { } static StyleFoo Bar(const T &_0) { - StyleFoo result; + StyleFoo result{}; ::new (&result.bar._0) (T)(_0); result.tag = Tag::Bar; return result; @@ -91,7 +91,7 @@ union StyleFoo { } static StyleFoo Baz(const StylePoint &_0) { - StyleFoo result; + StyleFoo result{}; ::new (&result.baz._0) (StylePoint)(_0); result.tag = Tag::Baz; return result; @@ -112,7 +112,7 @@ union StyleFoo { } static StyleFoo Bazz() { - StyleFoo result; + StyleFoo result{}; result.tag = Tag::Bazz; return result; } @@ -157,7 +157,7 @@ struct StyleBar { const StylePoint &y, const StylePoint &z, int32_t (*&u)(int32_t)) { - StyleBar result; + StyleBar result{}; ::new (&result.bar1.x) (int32_t)(x); ::new (&result.bar1.y) (StylePoint)(y); ::new (&result.bar1.z) (StylePoint)(z); @@ -181,7 +181,7 @@ struct StyleBar { } static StyleBar Bar2(const T &_0) { - StyleBar result; + StyleBar result{}; ::new (&result.bar2._0) (T)(_0); result.tag = Tag::Bar2; return result; @@ -202,7 +202,7 @@ struct StyleBar { } static StyleBar Bar3(const StylePoint &_0) { - StyleBar result; + StyleBar result{}; ::new (&result.bar3._0) (StylePoint)(_0); result.tag = Tag::Bar3; return result; @@ -223,7 +223,7 @@ struct StyleBar { } static StyleBar Bar4() { - StyleBar result; + StyleBar result{}; result.tag = Tag::Bar4; return result; } @@ -257,7 +257,7 @@ union StyleBaz { Baz2_Body baz2; static StyleBaz Baz1(const StyleBar &_0) { - StyleBaz result; + StyleBaz result{}; ::new (&result.baz1._0) (StyleBar)(_0); result.tag = Tag::Baz1; return result; @@ -278,7 +278,7 @@ union StyleBaz { } static StyleBaz Baz2(const StylePoint &_0) { - StyleBaz result; + StyleBaz result{}; ::new (&result.baz2._0) (StylePoint)(_0); result.tag = Tag::Baz2; return result; @@ -299,7 +299,7 @@ union StyleBaz { } static StyleBaz Baz3() { - StyleBaz result; + StyleBaz result{}; result.tag = Tag::Baz3; return result; } @@ -331,7 +331,7 @@ struct StyleTaz { }; static StyleTaz Taz1(const StyleBar &_0) { - StyleTaz result; + StyleTaz result{}; ::new (&result.taz1._0) (StyleBar)(_0); result.tag = Tag::Taz1; return result; @@ -352,7 +352,7 @@ struct StyleTaz { } static StyleTaz Taz2(const StyleBaz &_0) { - StyleTaz result; + StyleTaz result{}; ::new (&result.taz2._0) (StyleBaz)(_0); result.tag = Tag::Taz2; return result; @@ -373,7 +373,7 @@ struct StyleTaz { } static StyleTaz Taz3() { - StyleTaz result; + StyleTaz result{}; result.tag = Tag::Taz3; return result; } diff --git a/tests/rust/tagged_enum_value_init.rs b/tests/rust/tagged_enum_value_init.rs new file mode 100644 index 000000000..d4687431a --- /dev/null +++ b/tests/rust/tagged_enum_value_init.rs @@ -0,0 +1,18 @@ +/// Regression test: in C++ output, the generated static variant constructors of a +/// tagged enum must value-initialize their `result` local (`Foo result{};`) so that +/// the payload union is zero-initialized rather than left indeterminate. This avoids +/// false "uninitialized scalar" reports from static analysis for no-payload variants. +/// +/// cbindgen:derive-helper-methods=true +#[repr(C, u8)] +pub enum ValueInitEnum { + /// No payload: `result` is returned with only the tag set, so the union must be + /// value-initialized. + Empty, + /// Payload variant: constructed via placement new over the value-initialized union. + Number(i32), + Pair(i32, u64), +} + +#[no_mangle] +pub extern "C" fn root(e: ValueInitEnum) {}