@@ -456,7 +456,9 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
456456 const Decl *D1, const Decl *D2,
457457 const Decl *PrimaryDecl = nullptr ) {
458458 // 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+ //
460462 // FIXME: this should be handled on a case-by-case basis via tablegen in
461463 // Attr.td. There are multiple cases to consider: one declaration with the
462464 // attribute, another without it; different attribute syntax|spellings for
@@ -468,7 +470,7 @@ CheckStructurallyEquivalentAttributes(StructuralEquivalenceContext &Context,
468470 D1Attr = *D1->getAttrs ().begin ();
469471 if (D2->hasAttrs ())
470472 D2Attr = *D2->getAttrs ().begin ();
471- if (D1Attr || D2Attr) {
473+ if (( D1Attr || D2Attr) && !D1-> isImplicit () && !D2-> isImplicit () ) {
472474 const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
473475 Context.Diag2 (DiagnoseDecl->getLocation (),
474476 diag::warn_odr_tag_type_with_attributes)
@@ -884,12 +886,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
884886 // class comparison.
885887 if (T1->getTypeClass () == Type::Enum) {
886888 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
889890 } else if (T2->getTypeClass () == Type::Enum) {
890891 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
893893 }
894894 TC = Type::Builtin;
895895 } else
@@ -2093,6 +2093,48 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
20932093 !CheckStructurallyEquivalentAttributes (Context, D1, D2))
20942094 return false ;
20952095
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+
20962138 llvm::SmallVector<const EnumConstantDecl *, 8 > D1Enums, D2Enums;
20972139 auto CopyEnumerators =
20982140 [](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
0 commit comments