@@ -2113,6 +2113,48 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
21132113 !CheckStructurallyEquivalentAttributes (Context, D1, D2))
21142114 return false ;
21152115
2116+ // In C23, if one enumeration has a fixed underlying type, the other shall
2117+ // have a compatible fixed underlying type (6.2.7).
2118+ if (Context.LangOpts .C23 ) {
2119+ if (D1->isFixed () != D2->isFixed ()) {
2120+ if (Context.Complain ) {
2121+ Context.Diag2 (D2->getLocation (),
2122+ Context.getApplicableDiagnostic (
2123+ diag::err_odr_tag_type_inconsistent))
2124+ << Context.ToCtx .getTypeDeclType (D2)
2125+ << (&Context.FromCtx != &Context.ToCtx );
2126+ Context.Diag1 (D1->getLocation (),
2127+ D1->isFixed ()
2128+ ? diag::note_odr_fixed_underlying_type
2129+ : diag::note_odr_missing_fixed_underlying_type)
2130+ << D1;
2131+ Context.Diag2 (D2->getLocation (),
2132+ D2->isFixed ()
2133+ ? diag::note_odr_fixed_underlying_type
2134+ : diag::note_odr_missing_fixed_underlying_type)
2135+ << D2;
2136+ }
2137+ return false ;
2138+ }
2139+ if (D1->isFixed ()) {
2140+ assert (D2->isFixed () && " enums expected to have fixed underlying types" );
2141+ if (!IsStructurallyEquivalent (Context, D1->getIntegerType (),
2142+ D2->getIntegerType ())) {
2143+ if (Context.Complain ) {
2144+ Context.Diag2 (D2->getLocation (),
2145+ Context.getApplicableDiagnostic (
2146+ diag::err_odr_tag_type_inconsistent))
2147+ << Context.ToCtx .getTypeDeclType (D2)
2148+ << (&Context.FromCtx != &Context.ToCtx );
2149+ Context.Diag2 (D2->getLocation (),
2150+ diag::note_odr_incompatible_fixed_underlying_type)
2151+ << D2 << D2->getIntegerType () << D1->getIntegerType ();
2152+ }
2153+ return false ;
2154+ }
2155+ }
2156+ }
2157+
21162158 llvm::SmallVector<const EnumConstantDecl *, 8 > D1Enums, D2Enums;
21172159 auto CopyEnumerators =
21182160 [](auto &&Range, llvm::SmallVectorImpl<const EnumConstantDecl *> &Cont) {
0 commit comments