@@ -456,7 +456,9 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
456
456
const Decl *D1, const Decl *D2,
457
457
const Decl *PrimaryDecl = nullptr ) {
458
458
// If either declaration has an attribute on it, we treat the declarations
459
- // as not being structurally equivalent.
459
+ // as not being structurally equivalent unless both declarations are implicit
460
+ // (ones generated by the compiler like __NSConstantString_tag).
461
+ //
460
462
// FIXME: this should be handled on a case-by-case basis via tablegen in
461
463
// Attr.td. There are multiple cases to consider: one declaration with the
462
464
// attribute, another without it; different attribute syntax|spellings for
@@ -468,7 +470,7 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
468
470
D1Attr = *D1->getAttrs ().begin ();
469
471
if (D2->hasAttrs ())
470
472
D2Attr = *D2->getAttrs ().begin ();
471
- if (D1Attr || D2Attr) {
473
+ if (( D1Attr || D2Attr) && !D1-> isImplicit () && !D2-> isImplicit () ) {
472
474
const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
473
475
Context.Diag2 (DiagnoseDecl->getLocation (),
474
476
diag::warn_odr_tag_type_with_attributes)
@@ -884,12 +886,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
884
886
// class comparison.
885
887
if (T1->getTypeClass () == Type::Enum) {
886
888
T1 = T1->getAs <EnumType>()->getDecl ()->getIntegerType ();
887
- if (!T2->isBuiltinType () || T1.isNull ()) // Sanity check
888
- return false ;
889
+ assert (T2->isBuiltinType () && !T1.isNull ()); // Sanity check
889
890
} else if (T2->getTypeClass () == Type::Enum) {
890
891
T2 = T2->getAs <EnumType>()->getDecl ()->getIntegerType ();
891
- if (!T1->isBuiltinType () || T2.isNull ()) // Sanity check
892
- return false ;
892
+ assert (T1->isBuiltinType () && !T2.isNull ()); // Sanity check
893
893
}
894
894
TC = Type::Builtin;
895
895
} else
@@ -2093,6 +2093,48 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
2093
2093
!CheckStructurallyEquivalentAttributes (Context, D1, D2))
2094
2094
return false ;
2095
2095
2096
+ // In C23, if one enumeration has a fixed underlying type, the other shall
2097
+ // have a compatible fixed underlying type (6.2.7).
2098
+ if (Context.LangOpts .C23 ) {
2099
+ if (D1->isFixed () != D2->isFixed ()) {
2100
+ if (Context.Complain ) {
2101
+ Context.Diag2 (D2->getLocation (),
2102
+ Context.getApplicableDiagnostic (
2103
+ diag::err_odr_tag_type_inconsistent))
2104
+ << Context.ToCtx .getTypeDeclType (D2)
2105
+ << (&Context.FromCtx != &Context.ToCtx );
2106
+ Context.Diag1 (D1->getLocation (),
2107
+ D1->isFixed ()
2108
+ ? diag::note_odr_fixed_underlying_type
2109
+ : diag::note_odr_missing_fixed_underlying_type)
2110
+ << D1;
2111
+ Context.Diag2 (D2->getLocation (),
2112
+ D2->isFixed ()
2113
+ ? diag::note_odr_fixed_underlying_type
2114
+ : diag::note_odr_missing_fixed_underlying_type)
2115
+ << D2;
2116
+ }
2117
+ return false ;
2118
+ }
2119
+ if (D1->isFixed ()) {
2120
+ assert (D2->isFixed () && " enums expected to have fixed underlying types" );
2121
+ if (!IsStructurallyEquivalent (Context, D1->getIntegerType (),
2122
+ D2->getIntegerType ())) {
2123
+ if (Context.Complain ) {
2124
+ Context.Diag2 (D2->getLocation (),
2125
+ Context.getApplicableDiagnostic (
2126
+ diag::err_odr_tag_type_inconsistent))
2127
+ << Context.ToCtx .getTypeDeclType (D2)
2128
+ << (&Context.FromCtx != &Context.ToCtx );
2129
+ Context.Diag2 (D2->getLocation (),
2130
+ diag::note_odr_incompatible_fixed_underlying_type)
2131
+ << D2 << D2->getIntegerType () << D1->getIntegerType ();
2132
+ }
2133
+ return false ;
2134
+ }
2135
+ }
2136
+ }
2137
+
2096
2138
llvm::SmallVector<const EnumConstantDecl *, 8 > D1Enums, D2Enums;
2097
2139
auto CopyEnumerators =
2098
2140
[](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
0 commit comments