Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/class_decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -20662,7 +20662,18 @@ 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_underlying_type =
bit_field_type->variant.integer.has_explicit_enum_base ||
integer_type_is_scoped_enum(bit_field_type);

if (fixed_underlying_type) {
/* The enum has a fixed underlying type (an explicit enum base, or a
scoped enum), so the bit field has the signedness of that type,
regardless of the values of the enumeration constants. A bool
underlying type is treated as unsigned. */
use_signed = !enum_has_bool_underlying_type(bit_field_type) &&
int_type_is_signed(bit_field_type);
} /* if */
enum_con = enum_constants(bit_field_type);
if (enum_con == NULL) {
/* There are no enumeration constants, so no bits are needed to
Expand All @@ -20689,7 +20700,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_underlying_type) {
/* Enum bit fields are always unsigned (many ABIs require this). */
use_signed = FALSE;
bits_needed = bits_needed_largest;
Expand All @@ -20699,7 +20710,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_underlying_type) {
/* The signedness was determined above from the underlying type. */
} 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;
Expand Down Expand Up @@ -20734,17 +20747,11 @@ 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 &&
!bit_field_type->variant.integer.has_explicit_enum_base) {
!fixed_underlying_type) {
type_warning(ec_unsigned_enum_bit_field_with_signed_enumerator,
bit_field_type);
} /* if */
} /* if */
if (bit_field_type->variant.integer.has_explicit_enum_base) {
/* For an explicit underlying type, ignore the constants and just rely on
the underlying type. (We still potentially issue a warning above,
however.) */
use_signed = int_type_is_signed(bit_field_type);
} /* if */
*need_signed_type = use_signed;
} /* check_enum_type_for_bit_field */

Expand Down
2 changes: 2 additions & 0 deletions src/il_to_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 (their underlying type is fixed)
- 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);
Expand Down
18 changes: 18 additions & 0 deletions src/pending_changes/gh-485-scoped-enum-signedness.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[GH #485]
C-generating back end: scoped enumerations with a signed underlying type

The C-generating back end emitted a scoped enumeration as a C enum type. The
C compiler could give that enum type an unsigned representation, so a negative
value of a scoped enumeration whose underlying type is signed (int, when no
enum-base is specified) was converted to a large positive value. The
underlying integer type is now used instead, as it already was for enums with
an explicit enum-base. Bit fields of an enumeration type with a fixed
underlying type (a scoped enumeration, or one with an explicit enum-base) now
also take their signedness from that type, so a bit field of an enumeration
with underlying type bool can hold the value true. For example:

enum class E { ZERO = 0 };
int main() {
E e = static_cast<E>(-1);
long widened = static_cast<long>(e); // Was 4294967295, now -1.
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
struct { E e1 : 1; E e2; F f1 : 1; F f2; } s;
^

"Test_name.c", line 22: warning: bit field cannot contain all values of the enumerated type
struct { E e1 : 1; E e2; F f1 : 1; F f2; } s;
^

"Test_name.c", line 22: warning: bit field cannot contain all values of the enumerated type
struct { E e1 : 1; E e2; F f1 : 1; F f2; } s;
^

"Test_name.c", line 22: warning: variable "s" was set but never used
struct { E e1 : 1; E e2; F f1 : 1; F f2; } s;
^
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fe_only -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=130300 Test_name.c
"Test_name.c", line 8: warning: bit field cannot contain all values of the enumerated type
struct S { F f : 2; };
^

"Test_name.c", line 10: warning: bit field cannot contain all values of the enumerated type
struct T { E e : 2; };
^

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fe_only -DTEST_NUMBER=2 --c++20 --strict Test_name.c
"Test_name.c", line 8: warning: bit field cannot contain all values of the enumerated type
struct S { F f : 2; };
^

"Test_name.c", line 10: warning: bit field cannot contain all values of the enumerated type
struct T { E e : 2; };
^

1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-485.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=130300 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-485.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-485.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=2 --c++20 --strict Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-485.rto/default.2.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
10 changes: 10 additions & 0 deletions tests/tests/regressions/gh-485-bit-field-size.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//type:fp
//options_all:--c++20
//options:--g++ --gnu_version=130300:--strict
// An enum with a fixed underlying type gives its bit fields the signedness of
// that type, whether the enum is scoped or has an explicit enum base, so both
// of these bit fields are too small to hold the value 3.
enum class F { f3 = 3 };
struct S { F f : 2; };
enum E : int { e3 = 3 };
struct T { E e : 2; };
22 changes: 22 additions & 0 deletions tests/tests/regressions/gh-485.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//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; };
#ifdef __GNUC__
static_assert(__is_same(__underlying_type(Signed), int), "signed base");
static_assert(__is_same(__underlying_type(Empty), int), "empty base");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
#endif /* __GNUC__ */
volatile int minus_one = -1;
int main() {
Signed s = static_cast<Signed>(minus_one);
Empty e = static_cast<Empty>(minus_one);
Fields f{static_cast<Signed>(minus_one), Unsigned::zero, Boolean::one};
return static_cast<long>(s) != -1 || static_cast<long>(e) != -1 ||
static_cast<int>(f.s) != -1 || static_cast<unsigned>(f.u) != 0 ||
static_cast<int>(f.b) != 1;
}
Loading