Skip to content

Commit 3e5fc80

Browse files
authored
[C23] Fix treating unnamed records nested in different types as compatible. (llvm#162933)
Don't compare and accept unnamed records from different types only because they are defined in `RecordDecl` `DeclContext`. During recursive comparison don't reject unnamed records defined inside other ordered containers like Objective-C classes. rdar://161592007
1 parent a7f1910 commit 3e5fc80

File tree

4 files changed

+51
-16
lines changed

4 files changed

+51
-16
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ C23 Feature Support
191191
- Added ``FLT_SNAN``, ``DBL_SNAN``, and ``LDBL_SNAN`` to Clang's ``<float.h>``
192192
header in C23 and later modes. This implements
193193
`WG14 N2710 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2710.htm>`_.
194+
- Fixed accepting as compatible unnamed tag types with the same fields within
195+
the same translation unit but from different types.
194196

195197
Non-comprehensive list of changes in this release
196198
-------------------------------------------------

clang/lib/AST/ASTContext.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11581,6 +11581,12 @@ QualType ASTContext::mergeTagDefinitions(QualType LHS, QualType RHS) {
1158111581
if (LangOpts.CPlusPlus || !LangOpts.C23)
1158211582
return {};
1158311583

11584+
// Nameless tags are comparable only within outer definitions. At the top
11585+
// level they are not comparable.
11586+
const TagDecl *LTagD = LHS->castAsTagDecl(), *RTagD = RHS->castAsTagDecl();
11587+
if (!LTagD->getIdentifier() || !RTagD->getIdentifier())
11588+
return {};
11589+
1158411590
// C23, on the other hand, requires the members to be "the same enough", so
1158511591
// we use a structural equivalence check.
1158611592
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,19 +1763,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
17631763
// another anonymous structure or union, respectively, if their members
17641764
// fulfill the preceding requirements. ... Otherwise, the structure, union,
17651765
// or enumerated types are incompatible.
1766-
1767-
// Note: "the same tag" refers to the identifier for the structure; two
1768-
// structures without names are not compatible within a TU. In C23, if either
1769-
// declaration has no name, they're not equivalent. However, the paragraph
1770-
// after the bulleted list goes on to talk about compatibility of anonymous
1771-
// structure and union members, so this prohibition only applies to top-level
1772-
// declarations; if either declaration is not a member, they cannot be
1773-
// compatible.
1774-
if (Context.LangOpts.C23 && (!D1->getIdentifier() || !D2->getIdentifier()) &&
1775-
(!D1->getDeclContext()->isRecord() || !D2->getDeclContext()->isRecord()))
1776-
return false;
1777-
1778-
// Otherwise, check the names for equivalence.
17791766
if (!NameIsStructurallyEquivalent(*D1, *D2))
17801767
return false;
17811768

clang/test/C/C23/n3037.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,24 @@ void func2(PRODUCT(int, SUM(float, double)) y) { // c17-warning {{declaration of
3030

3131
struct foop { struct { int x; }; }; // c17-note {{previous definition is here}}
3232
struct foop { struct { int x; }; }; // c17-error {{redefinition of 'foop'}}
33+
// Test the field lookup compatibility isn't sufficient, the structure of types should be compatible.
34+
struct AnonymousStructNotMatchingFields { // c17-note {{previous definition is here}}
35+
struct { // c23-note {{field has name '' here}}
36+
int x;
37+
};
38+
};
39+
struct AnonymousStructNotMatchingFields { // c23-error {{type 'struct AnonymousStructNotMatchingFields' has incompatible definitions}} \
40+
c17-error {{redefinition of 'AnonymousStructNotMatchingFields'}}
41+
int x; // c23-note {{field has name 'x' here}}
42+
};
43+
3344
union barp { int x; float y; }; // c17-note {{previous definition is here}}
3445
union barp { int x; float y; }; // c17-error {{redefinition of 'barp'}}
3546
typedef struct q { int x; } q_t; // c17-note 2 {{previous definition is here}}
3647
typedef struct q { int x; } q_t; // c17-error {{redefinition of 'q'}} \
3748
c17-error-re {{typedef redefinition with different types ('struct (unnamed struct at {{.*}})' vs 'struct q')}}
49+
typedef struct { int x; } untagged_q_t; // both-note {{previous definition is here}}
50+
typedef struct { int x; } untagged_q_t; // both-error {{typedef redefinition with different types}}
3851
void func3(void) {
3952
struct S { int x; }; // c17-note {{previous definition is here}}
4053
struct T { struct S s; }; // c17-note {{previous definition is here}}
@@ -389,13 +402,40 @@ void nontag_both_in_params(struct { int i; } Arg1, struct { int i; } Arg2) {
389402
_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}}
390403
}
391404

392-
struct InnerAnonStruct {
405+
struct InnerUnnamedStruct {
393406
struct {
394407
int i;
395408
} untagged;
396-
} inner_anon_tagged;
409+
} inner_unnamed_tagged;
410+
_Static_assert(0 == _Generic(inner_unnamed_tagged.untagged, struct { int i; } : 1, default : 0));
397411

398-
_Static_assert(0 == _Generic(inner_anon_tagged.untagged, struct { int i; } : 1, default : 0));
412+
struct InnerUnnamedStruct_same {
413+
struct {
414+
int i;
415+
} untagged;
416+
};
417+
struct InnerUnnamedStruct_differentNaming {
418+
struct {
419+
int i;
420+
} untaggedDifferent;
421+
};
422+
struct InnerUnnamedStruct_differentShape {
423+
float x;
424+
struct {
425+
int i;
426+
} untagged;
427+
int y;
428+
};
429+
void compare_unnamed_struct_from_different_outer_type(
430+
struct InnerUnnamedStruct sameOuterType,
431+
struct InnerUnnamedStruct_same matchingType,
432+
struct InnerUnnamedStruct_differentNaming differentFieldName,
433+
struct InnerUnnamedStruct_differentShape differentType) {
434+
inner_unnamed_tagged.untagged = sameOuterType.untagged;
435+
inner_unnamed_tagged.untagged = matchingType.untagged; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
436+
inner_unnamed_tagged.untagged = differentFieldName.untaggedDifferent; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
437+
inner_unnamed_tagged.untagged = differentType.untagged; // both-error-re {{assigning to 'struct (unnamed struct at {{.*}})' from incompatible type 'struct (unnamed struct at {{.*}})'}}
438+
}
399439

400440
// Test the same thing with enumerations (test for unions is omitted because
401441
// unions and structures are both RecordDecl objects, whereas EnumDecl is not).

0 commit comments

Comments
 (0)