Skip to content

Commit cab2c77

Browse files
AaronBallmangoogle-yfyang
authored andcommitted
[C23] Handle type compatibility of unnamed records (llvm#141783)
At the top-level, both types need to have a tag in order for them to be compatible within the same TU in C23. An unnamed structure has no tag, so it cannot be compatible with another type within the TU. Fixes llvm#141724
1 parent 9642665 commit cab2c77

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,9 +1751,20 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
17511751
// fulfill the preceding requirements. ... Otherwise, the structure, union,
17521752
// or enumerated types are incompatible.
17531753

1754-
if (!NameIsStructurallyEquivalent(*D1, *D2)) {
1754+
// Note: "the same tag" refers to the identifier for the structure; two
1755+
// structures without names are not compatible within a TU. In C23, if either
1756+
// declaration has no name, they're not equivalent. However, the paragraph
1757+
// after the bulleted list goes on to talk about compatibility of anonymous
1758+
// structure and union members, so this prohibition only applies to top-level
1759+
// declarations; if either declaration is not a member, they cannot be
1760+
// compatible.
1761+
if (Context.LangOpts.C23 && (!D1->getIdentifier() || !D2->getIdentifier()) &&
1762+
(!D1->getDeclContext()->isRecord() || !D2->getDeclContext()->isRecord()))
1763+
return false;
1764+
1765+
// Otherwise, check the names for equivalence.
1766+
if (!NameIsStructurallyEquivalent(*D1, *D2))
17551767
return false;
1756-
}
17571768

17581769
if (D1->isUnion() != D2->isUnion()) {
17591770
if (Context.Complain) {

clang/test/C/C23/n3037.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ struct quals_matter { // c17-note {{previous definition is here}}
140140
};
141141

142142
struct quals_matter { // c17-error {{redefinition of 'quals_matter'}} \
143-
c23-error {{type 'struct quals_matter' has incompatible definitions}}
143+
c23-error {{type 'struct quals_matter' has incompatible definitions}}
144144
const int x; // c23-note {{field 'x' has type 'const int' here}}
145145
};
146146

@@ -359,3 +359,45 @@ struct alignment { // c17-error {{redefinition of 'alignment'}} \
359359
c23-error {{type 'struct alignment' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
360360
int x;
361361
};
362+
363+
// Both structures need to have a tag in order to be compatible within the same
364+
// translation unit.
365+
struct {int i;} nontag;
366+
struct tag {int i;} tagged; // c17-note 2 {{previous definition is here}}
367+
368+
_Static_assert(1 == _Generic(tagged, struct tag {int i;}:1, default:0)); // c17-error {{redefinition of 'tag'}} \
369+
c17-error {{static assertion failed}}
370+
_Static_assert(0 == _Generic(tagged, struct {int i;}:1, default:0));
371+
_Static_assert(0 == _Generic(nontag, struct tag {int i;}:1, default:0)); // c17-error {{redefinition of 'tag'}}
372+
// That means these two structures are not actually compatible; see GH141724.
373+
_Static_assert(0 == _Generic(nontag, struct {int i;}:1, default:0));
374+
375+
// Also test the behavior within a function (so the declaration context is not
376+
// at the translation unit level).
377+
void nontag_func_test(void) {
378+
struct { int i; } test;
379+
_Static_assert(0 == _Generic(test, struct { int i; } : 1, default : 0));
380+
}
381+
382+
// Same kind of test, but this time for a declaration in the parameter list.
383+
void nontag_param_test(struct { int i; } herp) {
384+
_Static_assert(0 == _Generic(herp, struct { int i; } : 1, default : 0));
385+
}
386+
387+
// Same kind of test, but demonstrating that these still aren't compatible.
388+
void nontag_both_in_params(struct { int i; } Arg1, struct { int i; } Arg2) {
389+
_Static_assert(0 == _Generic(__typeof__(Arg1), __typeof__(Arg2) : 1, default : 0)); // both-warning {{passing a type argument as the first operand to '_Generic' is a C2y extension}}
390+
}
391+
392+
struct InnerAnonStruct {
393+
struct {
394+
int i;
395+
} untagged;
396+
} inner_anon_tagged;
397+
398+
_Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1, default : 0));
399+
400+
// Test the same thing with enumerations (test for unions is omitted because
401+
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).
402+
enum { E_Untagged1 } nontag_enum; // both-note {{previous definition is here}}
403+
_Static_assert(0 == _Generic(nontag_enum, enum { E_Untagged1 } : 1, default : 0)); // both-error {{redefinition of enumerator 'E_Untagged1'}}

0 commit comments

Comments
 (0)