From 08a3863d602b603b2106dcea036b445d0baacc0c Mon Sep 17 00:00:00 2001 From: Ehab Maali <107338078+71iq@users.noreply.github.com> Date: Sun, 1 Nov 2026 10:46:47 +0200 Subject: [PATCH 1/3] Preserve scoped enum underlying signedness [EDGcpfe/28934] --- src/class_decl.c | 13 +++++++++++-- src/il_to_str.c | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/class_decl.c b/src/class_decl.c index 4e16b53c9f..766146b5c4 100644 --- a/src/class_decl.c +++ b/src/class_decl.c @@ -20662,7 +20662,13 @@ must be unsigned. a_constant_ptr smallest, largest; size_t bits_needed, bits_needed_largest, bits_needed_smallest; a_constant_ptr enum_con; + a_boolean fixed_scoped_base = + integer_type_is_scoped_enum(bit_field_type); + if (fixed_scoped_base) { + use_signed = !enum_has_bool_underlying_type(bit_field_type) && + int_kind_is_signed[bit_field_type->variant.integer.int_kind]; + } /* if */ enum_con = enum_constants(bit_field_type); if (enum_con == NULL) { /* There are no enumeration constants, so no bits are needed to @@ -20689,7 +20695,7 @@ must be unsigned. bits_required_to_represent_integer_constant(largest); /* See if the smallest value is negative. */ smallest_is_negative = (sign_of_integer_constant(smallest) < 0); - if (targ_enum_bit_fields_are_always_unsigned) { + if (targ_enum_bit_fields_are_always_unsigned && !fixed_scoped_base) { /* Enum bit fields are always unsigned (many ABIs require this). */ use_signed = FALSE; bits_needed = bits_needed_largest; @@ -20699,7 +20705,9 @@ must be unsigned. for efficiency reasons: if the enum values just fit in the bit field size, an unsigned field might be necessary even though a signed type was a good choice for the enum type. */ - if (smallest_is_negative) { + if (fixed_scoped_base) { + /* Scoped enum bit fields retain their fixed underlying signedness. */ + } else if (smallest_is_negative) { /* Some enum values are negative, so a signed type is required. The enum type must already be signed. */ use_signed = TRUE; @@ -20734,6 +20742,7 @@ must be unsigned. pos_warning(ec_enum_bit_field_too_small, &error_position); } /* if */ if (targ_enum_bit_fields_are_always_unsigned && smallest_is_negative && + !fixed_scoped_base && !bit_field_type->variant.integer.has_explicit_enum_base) { type_warning(ec_unsigned_enum_bit_field_with_signed_enumerator, bit_field_type); diff --git a/src/il_to_str.c b/src/il_to_str.c index 1abaece6e5..1977d91549 100644 --- a/src/il_to_str.c +++ b/src/il_to_str.c @@ -2391,11 +2391,13 @@ by octl. back end. Specifically: - don't generate enums when generating pcc mode - don't generate enums with an explicit underlying type + - don't generate scoped enums with a fixed int underlying type - don't generate empty enums (valid in C++, but not in C) In these cases, the underlying integer type is used instead. */ !(is_for_c_gen_be(octl) && (octl->gen_pcc_code || type->variant.integer.has_explicit_enum_base || + type->variant.integer.is_scoped_enum || enum_constants(type) == NULL))) { /* Output a reference to the enum type. */ form_tag_reference(type, octl); From 43a80889a09fc576e8ecba5ea5a91a3fc5d3f671 Mon Sep 17 00:00:00 2001 From: Ehab Maali <107338078+71iq@users.noreply.github.com> Date: Sun, 1 Nov 2026 10:46:55 +0200 Subject: [PATCH 2/3] Add scoped enum signedness regression tests [EDGcpfe/28934] --- tests/tests/regressions/edg-28934.sft.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/tests/regressions/edg-28934.sft.cpp diff --git a/tests/tests/regressions/edg-28934.sft.cpp b/tests/tests/regressions/edg-28934.sft.cpp new file mode 100644 index 0000000000..d32a2bb6ac --- /dev/null +++ b/tests/tests/regressions/edg-28934.sft.cpp @@ -0,0 +1,20 @@ +//type:rp +//options_all:--c++20 +//options:--g++ --gnu_version=130300:--strict +// Test the emitted C representation as well as the front-end type. +enum class Signed { zero = 0 }; +enum struct Empty {}; +enum class Unsigned : unsigned { zero = 0 }; +enum class Boolean : bool { zero = false, one = true }; +struct Fields { Signed s : 3; Unsigned u : 3; Boolean b : 1; }; +static_assert(__is_same(__underlying_type(Signed), int), "signed base"); +static_assert(__is_same(__underlying_type(Empty), int), "empty base"); +volatile int minus_one = -1; +int main() { + Signed s = static_cast(minus_one); + Empty e = static_cast(minus_one); + Fields f{static_cast(minus_one), Unsigned::zero, Boolean::one}; + return static_cast(s) != -1 || static_cast(e) != -1 || + static_cast(f.s) != -1 || static_cast(f.u) != 0 || + !static_cast(f.b); +} From 79765464b6c4f4926b77b575e48fb8d1c0924021 Mon Sep 17 00:00:00 2001 From: Ehab Maali <107338078+71iq@users.noreply.github.com> Date: Sun, 1 Nov 2026 10:49:17 +0200 Subject: [PATCH 3/3] Document scoped enum signedness fix [GH #456] --- src/pending_changes/edg-28934-scoped-enum-signedness.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/pending_changes/edg-28934-scoped-enum-signedness.txt diff --git a/src/pending_changes/edg-28934-scoped-enum-signedness.txt b/src/pending_changes/edg-28934-scoped-enum-signedness.txt new file mode 100644 index 0000000000..aa82f79b7a --- /dev/null +++ b/src/pending_changes/edg-28934-scoped-enum-signedness.txt @@ -0,0 +1,6 @@ +[GH #456] +Preserve scoped enum underlying signedness + +The C-generating back end now uses the underlying integer type for scoped +enumerations. Scoped enumeration bit fields retain their fixed underlying +signedness.