diff --git a/src/class_decl.c b/src/class_decl.c index 4e16b53c9f..e4d09ca43e 100644 --- a/src/class_decl.c +++ b/src/class_decl.c @@ -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 @@ -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; @@ -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; @@ -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 */ diff --git a/src/il_to_str.c b/src/il_to_str.c index 1abaece6e5..701b026422 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 (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); diff --git a/src/pending_changes/gh-485-scoped-enum-signedness.txt b/src/pending_changes/gh-485-scoped-enum-signedness.txt new file mode 100644 index 0000000000..7215761202 --- /dev/null +++ b/src/pending_changes/gh-485-scoped-enum-signedness.txt @@ -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(-1); + long widened = static_cast(e); // Was 4294967295, now -1. + } diff --git a/tests/tests/imported/clang/cpp/SemaCXX/.warn-msvc-enum-bitfield.rto/default.1.1.txt b/tests/tests/imported/clang/cpp/SemaCXX/.warn-msvc-enum-bitfield.rto/default.1.1.txt index 948d363a86..fd7cb4eb5a 100644 --- a/tests/tests/imported/clang/cpp/SemaCXX/.warn-msvc-enum-bitfield.rto/default.1.1.txt +++ b/tests/tests/imported/clang/cpp/SemaCXX/.warn-msvc-enum-bitfield.rto/default.1.1.txt @@ -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; ^ diff --git a/tests/tests/regressions/.gh-485-bit-field-size.rto/default.1.1.txt b/tests/tests/regressions/.gh-485-bit-field-size.rto/default.1.1.txt new file mode 100644 index 0000000000..2427e8ddad --- /dev/null +++ b/tests/tests/regressions/.gh-485-bit-field-size.rto/default.1.1.txt @@ -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; }; + ^ + diff --git a/tests/tests/regressions/.gh-485-bit-field-size.rto/default.2.1.txt b/tests/tests/regressions/.gh-485-bit-field-size.rto/default.2.1.txt new file mode 100644 index 0000000000..6baffc987f --- /dev/null +++ b/tests/tests/regressions/.gh-485-bit-field-size.rto/default.2.1.txt @@ -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; }; + ^ + diff --git a/tests/tests/regressions/.gh-485.rto/default.1.1.txt b/tests/tests/regressions/.gh-485.rto/default.1.1.txt new file mode 100644 index 0000000000..285a052375 --- /dev/null +++ b/tests/tests/regressions/.gh-485.rto/default.1.1.txt @@ -0,0 +1 @@ +eccp -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=130300 Test_name.c diff --git a/tests/tests/regressions/.gh-485.rto/default.1.2.txt b/tests/tests/regressions/.gh-485.rto/default.1.2.txt new file mode 100644 index 0000000000..ff2642c6d5 --- /dev/null +++ b/tests/tests/regressions/.gh-485.rto/default.1.2.txt @@ -0,0 +1 @@ +./a.out diff --git a/tests/tests/regressions/.gh-485.rto/default.2.1.txt b/tests/tests/regressions/.gh-485.rto/default.2.1.txt new file mode 100644 index 0000000000..8f7e41e84c --- /dev/null +++ b/tests/tests/regressions/.gh-485.rto/default.2.1.txt @@ -0,0 +1 @@ +eccp -DTEST_NUMBER=2 --c++20 --strict Test_name.c diff --git a/tests/tests/regressions/.gh-485.rto/default.2.2.txt b/tests/tests/regressions/.gh-485.rto/default.2.2.txt new file mode 100644 index 0000000000..ff2642c6d5 --- /dev/null +++ b/tests/tests/regressions/.gh-485.rto/default.2.2.txt @@ -0,0 +1 @@ +./a.out diff --git a/tests/tests/regressions/gh-485-bit-field-size.sft.cpp b/tests/tests/regressions/gh-485-bit-field-size.sft.cpp new file mode 100644 index 0000000000..aa635ffeea --- /dev/null +++ b/tests/tests/regressions/gh-485-bit-field-size.sft.cpp @@ -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; }; diff --git a/tests/tests/regressions/gh-485.sft.cpp b/tests/tests/regressions/gh-485.sft.cpp new file mode 100644 index 0000000000..809e33d009 --- /dev/null +++ b/tests/tests/regressions/gh-485.sft.cpp @@ -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"); +#endif /* __GNUC__ */ +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) != 1; +}