Skip to content
Open
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
30 changes: 30 additions & 0 deletions src/Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
Version 7.1

10/16/26
__has_unique_object_representations fixes

A base class that declares no nonstatic data members of its own but inherits
some (and is therefore not empty) was treated as an empty base class. Its
size was then counted as tail padding, so the trait was incorrectly FALSE for
classes derived from it. For example, with --g++:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is not actually gcc specific, but also applies to default/standard mode, so should just have an example with --c++11.


struct A { char i; };
struct B : A {};
struct C : B {};
static_assert(__has_unique_object_representations(C)); // Previously
// failed.

This is now fixed. In addition, the following version-dependent behaviors
of g++ and Clang are now emulated:

- An unnamed bit-field of nonzero width is treated as padding (making the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should the behavior for standard-mode be in this case?

result FALSE) in g++ 10.5, 11.4, 12.3, 13.1 and later, and in Clang 17
and later. For example:

struct S { char a; char : 8; char b; };
// __has_unique_object_representations(S) is now FALSE in those modes.

- Volatile-qualified non-class types (e.g., "volatile int") are treated as
not having unique object representations only in g++ modes before
version 10 (previously this was done for all g++ versions, including
Clang modes).


10/16/26 [GH #229]
Improper handling of "if constexpr" substatements

Expand Down
36 changes: 31 additions & 5 deletions src/folding.c
Original file line number Diff line number Diff line change
Expand Up @@ -8939,6 +8939,26 @@ with cssp are trivial.
} /* all_copy_constructors_trivial */


static a_boolean unnamed_bit_field_is_padding(void)
/*
Return TRUE if an unnamed bit-field of non-zero width is considered padding
by type_has_unique_object_representations. That is the case for Clang 17
and later and for g++ 13.1 and later (the change was also backported to
g++ 10.5, 11.4, and 12.3).
*/
{
a_boolean result = FALSE;

if (clang_mode) {
result = clang_version >= 170000;
} else if (gpp_mode) {
result = gnu_version >= 120300 ||
(gnu_version >= 110400 && gnu_version < 120000) ||
(gnu_version >= 100500 && gnu_version < 110000);
} /* if */
return result;
} /* unnamed_bit_field_is_padding */

static a_boolean type_has_unique_object_representations(
a_type_ptr type,
a_targ_size_t *after_base_members,
Expand Down Expand Up @@ -8999,8 +9019,7 @@ architectures for which these assumptions are not valid.
if (bcp->offset != end_of_last_subobject) {
/* There's padding between base class subobjects. */
result = FALSE;
} else if (bcp->type->variant.class_struct_union.field_list ==
NULL) {
} else if (is_empty_class_type(bcp->type)) {
/* An empty base class: treat it as having size 0 and do not
check type_has_unique_object_representation, since an empty
class will consist of nothing but padding. If it was not
Expand Down Expand Up @@ -9035,6 +9054,12 @@ architectures for which these assumptions are not valid.
same size or (for g++, not clang) if a bit-field is shorter
than the size of the union. */
result = FALSE;
} else if (field->is_bit_field && field->bit_size != 0 &&
unnamed_bit_field_is_padding() &&
field->source_corresp.assoc_info ==
(char *)get_unnamed_field_symbol()) {
/* An unnamed bit-field of non-zero width is padding. */
result = FALSE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that need to be restricted to non-union class types?

} else if (field->offset != end_of_last_subobject ||
field->offset_bit_remainder != bit_offset_of_end) {
/* There's padding between nonstatic data members. */
Expand Down Expand Up @@ -9078,9 +9103,10 @@ architectures for which these assumptions are not valid.
} /* if */
} /* if */
} /* if */
} else if (gpp_mode && is_volatile_qualified_type(orig_type)) {
/* g++ treats volatile-qualified types as not having unique object
representations. */
} else if (gpp_mode && !clang_mode && gnu_version < 100000 &&
is_volatile_qualified_type(orig_type)) {
/* g++ before version 10 treats volatile-qualified non-class types as
not having unique object representations. */
result = FALSE;
} else {
/* A non-class type. */
Expand Down
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -6872,7 +6872,6 @@ gnu/cpp/cpp1z/fold1/test.mft.cpp:fn:-DTEST_NUMBER=1 --c++17:FAIL:
gnu/cpp/cpp1z/fold5.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp1z/fold6.sft.cpp:fn:-DTEST_NUMBER=1 --c++17:FAIL:
gnu/cpp/cpp1z/has-unique-obj-representations1.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp1z/has-unique-obj-representations3.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp1z/inh-ctor1.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp1z/inh-ctor33.sft.cpp:lp:-DTEST_NUMBER=1 --c++11:FAIL:
gnu/cpp/cpp1z/inh-ctor38.sft.cpp:rp:-DTEST_NUMBER=1 --c++11:FAIL:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,38 +375,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(!__is_trivially_destructible(AnIncompleteType[1])); // expected-error {{incomplete type}}
^

"Test_name.c", line 3482: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(volatile int), "integrals are");
^

"Test_name.c", line 3483: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(const volatile int), "integrals are");
^

"Test_name.c", line 3502: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(volatile FP), "even function pointers");
^

"Test_name.c", line 3503: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(const volatile FP), "even function pointers");
^

"Test_name.c", line 3507: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMF), "and pointer to members");
^

"Test_name.c", line 3508: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMF), "and pointer to members");
^

"Test_name.c", line 3512: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMD), "and pointer to members");
^

"Test_name.c", line 3513: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMD), "and pointer to members");
^

"Test_name.c", line 3544: error: an incomplete class type is not allowed
static_assert(!__has_unique_object_representations(AnIncompleteType));
^
Expand All @@ -431,14 +399,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
char d : 1;
^

"Test_name.c", line 3777: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfield), "Bitfield padding");
^

"Test_name.c", line 3778: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfieldPacked), "Bitfield padding");
^

"Test_name.c", line 3790: warning: declared size for bit field is larger than the size of the bit field type; truncated to 8 bits
bool b : 16;
^
Expand Down Expand Up @@ -695,5 +655,5 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
#error Programming model currently unsupported; please add a new entry.
^

124 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
114 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
Compilation terminated.
Original file line number Diff line number Diff line change
Expand Up @@ -379,38 +379,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(!__is_trivially_destructible(AnIncompleteType[1])); // expected-error {{incomplete type}}
^

"Test_name.c", line 3482: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(volatile int), "integrals are");
^

"Test_name.c", line 3483: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(const volatile int), "integrals are");
^

"Test_name.c", line 3502: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(volatile FP), "even function pointers");
^

"Test_name.c", line 3503: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(const volatile FP), "even function pointers");
^

"Test_name.c", line 3507: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMF), "and pointer to members");
^

"Test_name.c", line 3508: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMF), "and pointer to members");
^

"Test_name.c", line 3512: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMD), "and pointer to members");
^

"Test_name.c", line 3513: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMD), "and pointer to members");
^

"Test_name.c", line 3544: error: an incomplete class type is not allowed
static_assert(!__has_unique_object_representations(AnIncompleteType));
^
Expand All @@ -435,14 +403,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
char d : 1;
^

"Test_name.c", line 3777: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfield), "Bitfield padding");
^

"Test_name.c", line 3778: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfieldPacked), "Bitfield padding");
^

"Test_name.c", line 3790: warning: declared size for bit field is larger than the size of the bit field type; truncated to 8 bits
bool b : 16;
^
Expand Down Expand Up @@ -695,5 +655,5 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
#error Programming model currently unsupported; please add a new entry.
^

124 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
114 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
Compilation terminated.
Original file line number Diff line number Diff line change
Expand Up @@ -363,38 +363,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(!__is_trivially_destructible(AnIncompleteType[1])); // expected-error {{incomplete type}}
^

"Test_name.c", line 3482: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(volatile int), "integrals are");
^

"Test_name.c", line 3483: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(const volatile int), "integrals are");
^

"Test_name.c", line 3502: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(volatile FP), "even function pointers");
^

"Test_name.c", line 3503: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(const volatile FP), "even function pointers");
^

"Test_name.c", line 3507: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMF), "and pointer to members");
^

"Test_name.c", line 3508: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMF), "and pointer to members");
^

"Test_name.c", line 3512: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMD), "and pointer to members");
^

"Test_name.c", line 3513: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMD), "and pointer to members");
^

"Test_name.c", line 3544: error: an incomplete class type is not allowed
static_assert(!__has_unique_object_representations(AnIncompleteType));
^
Expand All @@ -419,14 +387,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
char d : 1;
^

"Test_name.c", line 3777: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfield), "Bitfield padding");
^

"Test_name.c", line 3778: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfieldPacked), "Bitfield padding");
^

"Test_name.c", line 3790: warning: declared size for bit field is larger than the size of the bit field type; truncated to 8 bits
bool b : 16;
^
Expand Down Expand Up @@ -635,5 +595,5 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
#error Programming model currently unsupported; please add a new entry.
^

124 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
114 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
Compilation terminated.
Original file line number Diff line number Diff line change
Expand Up @@ -359,38 +359,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(!__is_trivially_destructible(AnIncompleteType[1])); // expected-error {{incomplete type}}
^

"Test_name.c", line 3482: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(volatile int), "integrals are");
^

"Test_name.c", line 3483: error: static assertion failed with "integrals are"
static_assert(__has_unique_object_representations(const volatile int), "integrals are");
^

"Test_name.c", line 3502: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(volatile FP), "even function pointers");
^

"Test_name.c", line 3503: error: static assertion failed with "even function pointers"
static_assert(__has_unique_object_representations(const volatile FP), "even function pointers");
^

"Test_name.c", line 3507: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMF), "and pointer to members");
^

"Test_name.c", line 3508: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMF), "and pointer to members");
^

"Test_name.c", line 3512: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(volatile PMD), "and pointer to members");
^

"Test_name.c", line 3513: error: static assertion failed with "and pointer to members"
static_assert(__has_unique_object_representations(const volatile PMD), "and pointer to members");
^

"Test_name.c", line 3544: error: an incomplete class type is not allowed
static_assert(!__has_unique_object_representations(AnIncompleteType));
^
Expand All @@ -415,14 +383,6 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
char d : 1;
^

"Test_name.c", line 3777: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfield), "Bitfield padding");
^

"Test_name.c", line 3778: error: static assertion failed with "Bitfield padding"
static_assert(!__has_unique_object_representations(UnnamedBitfieldPacked), "Bitfield padding");
^

"Test_name.c", line 3790: warning: declared size for bit field is larger than the size of the bit field type; truncated to 8 bits
bool b : 16;
^
Expand Down Expand Up @@ -635,5 +595,5 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
#error Programming model currently unsupported; please add a new entry.
^

124 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
114 errors and 1 catastrophic error detected in the compilation of "Test_name.c".
Compilation terminated.
Original file line number Diff line number Diff line change
@@ -1,16 +1 @@
fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics --c++11 Test_name.c
"Test_name.c", line 10: error: static assertion failed with ""
static_assert (__has_unique_object_representations (U) == false, "");
^
"Test_name.c", line 10: note: the final comparison was 1 == 0
static_assert (__has_unique_object_representations (U) == false, "");
^

"Test_name.c", line 11: error: static assertion failed with ""
static_assert (__has_unique_object_representations (V) == false, "");
^
"Test_name.c", line 11: note: the final comparison was 1 == 0
static_assert (__has_unique_object_representations (V) == false, "");
^

2 errors detected in the compilation of "Test_name.c".
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++17 --g++ --gnu_version=150100 Test_name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 --c++17 --clang --clang_version=190000 Test_name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//remark:has_unique_object_representations with a base that has no direct fields
//type:fp
//options_all:--c++17
//options:--g++ --gnu_version=150100:--clang --clang_version=190000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test case should also test standard mode (and ideally, also MSVC modes).

Also need to have test coverage for the other code changes (unnamed bit-field, volatile qualifiers). Should also test each version condition from the source code.


// B declares no fields but is not empty (it inherits A::i), so it must not
// be skipped as an empty base when checking C.
struct A { char i; };
struct B : A {};
struct C : B {};
struct D : C {};
struct E {};
struct F : E, B {}; // Real empty base is still skipped.
struct G : B { char j; };
struct P { int i; char c; }; // Tail padding.
struct Q : P {};

static_assert(__has_unique_object_representations(B), "B");
static_assert(__has_unique_object_representations(C), "C");
static_assert(__has_unique_object_representations(D), "D");
static_assert(__has_unique_object_representations(F), "F");
static_assert(__has_unique_object_representations(G), "G");
static_assert(!__has_unique_object_representations(P), "P");
static_assert(!__has_unique_object_representations(Q), "Q");