rnk created this revision.
rnk added reviewers: hans, rsmith, STL_MSFT.
Herald added a project: clang.

These three modes need to range check enumerator values differently than
C++ does. Before this change, we had two codepaths doing the same thing
in two cases:

1. enum complete (ObjC fixed & Microsoft unfixed)
2. enum incomplete (C99 unfixed)

Now we share the code, but have separate diagnostic paths.

The main functional change is that -fno-ms-compatibility now no longer
sends us down the hard error diagnostic code path for ObjC fixed enums.
Instead, complete-but-not-fixed MS enums go down the C99 codepath which
issues a -Wmicrosoft-enum-value warning about the enum value not being
representable as an 'int'. This was highlighted as the single largest
blocker to compiling windows.h with -fno-ms-compatibility, so this
should make that feasible.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D67304

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDecl.cpp
  clang/test/Sema/MicrosoftCompatibility.c
  clang/test/SemaCXX/MicrosoftCompatibility.cpp

Index: clang/test/SemaCXX/MicrosoftCompatibility.cpp
===================================================================
--- clang/test/SemaCXX/MicrosoftCompatibility.cpp
+++ clang/test/SemaCXX/MicrosoftCompatibility.cpp
@@ -235,8 +235,8 @@
 
 enum ENUM2 {
 	ENUM2_a = (enum ENUM2) 4,
-	ENUM2_b = 0x9FFFFFFF, // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
-	ENUM2_c = 0x100000000 // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
+	ENUM2_b = 0x9FFFFFFF, // expected-warning {{the Microsoft ABI restricts unfixed enumerator values to the range of 'int' (2684354559 is too large)}}
+	ENUM2_c = 0x100000000 // expected-warning {{the Microsoft ABI restricts unfixed enumerator values to the range of 'int' (4294967296 is too large)}}
 };
 
 namespace NsEnumForwardDecl {
Index: clang/test/Sema/MicrosoftCompatibility.c
===================================================================
--- clang/test/Sema/MicrosoftCompatibility.c
+++ clang/test/Sema/MicrosoftCompatibility.c
@@ -7,8 +7,8 @@
 
 enum ENUM2 {
   ENUM2_a = (enum ENUM2) 4,
-  ENUM2_b = 0x9FFFFFFF, // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
-  ENUM2_c = 0x100000000 // expected-warning {{enumerator value is not representable in the underlying type 'int'}}
+  ENUM2_b = 0x9FFFFFFF, // expected-warning {{the Microsoft ABI restricts unfixed enumerator values to the range of 'int' (2684354559 is too large)}}
+  ENUM2_c = 0x100000000 // expected-warning {{the Microsoft ABI restricts unfixed enumerator values to the range of 'int' (4294967296 is too large)}}
 };
 
 __declspec(noreturn) void f6( void ) {
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -14423,7 +14423,7 @@
                                           UPPC_FixedUnderlyingType))
         EnumUnderlying = Context.IntTy.getTypePtr();
 
-    } else if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
+    } else if (Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment()) {
       // For MSVC ABI compatibility, unfixed enums must use an underlying type
       // of 'int'. However, if this is an unfixed forward declaration, don't set
       // the underlying type unless the user enables -fms-compatibility. This
@@ -16552,8 +16552,7 @@
     if (Enum->isDependentType() || Val->isTypeDependent())
       EltTy = Context.DependentTy;
     else {
-      if (getLangOpts().CPlusPlus11 && Enum->isFixed() &&
-          !getLangOpts().MSVCCompat) {
+      if (getLangOpts().CPlusPlus11 && Enum->isFixed()) {
         // C++11 [dcl.enum]p5: If the underlying type is fixed, [...] the
         // constant-expression in the enumerator-definition shall be a converted
         // constant expression of the underlying type.
@@ -16570,25 +16569,7 @@
                                                          &EnumVal).get())) {
         // C99 6.7.2.2p2: Make sure we have an integer constant expression.
       } else {
-        if (Enum->isComplete()) {
-          EltTy = Enum->getIntegerType();
-
-          // In Obj-C and Microsoft mode, require the enumeration value to be
-          // representable in the underlying type of the enumeration. In C++11,
-          // we perform a non-narrowing conversion as part of converted constant
-          // expression checking.
-          if (!isRepresentableIntegerValue(Context, EnumVal, EltTy)) {
-            if (getLangOpts().MSVCCompat) {
-              Diag(IdLoc, diag::ext_enumerator_too_large) << EltTy;
-              Val = ImpCastExprToType(Val, EltTy, CK_IntegralCast).get();
-            } else
-              Diag(IdLoc, diag::err_enumerator_too_large) << EltTy;
-          } else
-            Val = ImpCastExprToType(Val, EltTy,
-                                    EltTy->isBooleanType() ?
-                                    CK_IntegralToBoolean : CK_IntegralCast)
-                    .get();
-        } else if (getLangOpts().CPlusPlus) {
+        if (getLangOpts().CPlusPlus && !Enum->isComplete()) {
           // C++11 [dcl.enum]p5:
           //   If the underlying type is not fixed, the type of each enumerator
           //   is the type of its initializing value:
@@ -16600,17 +16581,37 @@
           //   The expression that defines the value of an enumeration constant
           //   shall be an integer constant expression that has a value
           //   representable as an int.
+          //
+          // Microsoft: The enum type will already be complete and it will be
+          // 'int'. Use the C99 codepath.
+          //
+          // ObjC: Check that enumerators match any underlying type if present.
+          EltTy = Enum->isComplete() ? Enum->getIntegerType() : Context.IntTy;
 
-          // Complain if the value is not representable in an int.
-          if (!isRepresentableIntegerValue(Context, EnumVal, Context.IntTy))
-            Diag(IdLoc, diag::ext_enum_value_not_int)
-              << EnumVal.toString(10) << Val->getSourceRange()
-              << (EnumVal.isUnsigned() || EnumVal.isNonNegative());
-          else if (!Context.hasSameType(Val->getType(), Context.IntTy)) {
-            // Force the type of the expression to 'int'.
-            Val = ImpCastExprToType(Val, Context.IntTy, CK_IntegralCast).get();
+          if (!isRepresentableIntegerValue(Context, EnumVal, EltTy)) {
+            bool IsMSVC =
+                Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment();
+            if (!Enum->isComplete() || IsMSVC) {
+              // Complain if the value is not representable in an int in C99 or
+              // MS mode.
+              unsigned DiagId = IsMSVC ? diag::warn_ms_enum_value_not_int
+                                       : diag::ext_enum_value_not_int;
+              Diag(IdLoc, DiagId)
+                  << EnumVal.toString(10) << Val->getSourceRange()
+                  << (EnumVal.isUnsigned() || EnumVal.isNonNegative());
+            } else {
+              // Complain if the enumerator doesn't fit the underlying type in
+              // ObjC.
+              Diag(IdLoc, diag::err_enumerator_too_large) << EltTy;
+            }
+          } else if (!Context.hasSameType(Val->getType(), EltTy)) {
+            // Force the type of the expression to the underlying enum type.
+            Val =
+                ImpCastExprToType(Val, EltTy,
+                                  EltTy->isBooleanType() ? CK_IntegralToBoolean
+                                                         : CK_IntegralCast)
+                    .get();
           }
-          EltTy = Val->getType();
         }
       }
     }
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2242,9 +2242,6 @@
   "non-integral type %0 is an invalid underlying type">;
 def err_enumerator_too_large : Error<
   "enumerator value is not representable in the underlying type %0">;
-def ext_enumerator_too_large : Extension<
-  "enumerator value is not representable in the underlying type %0">,
-  InGroup<MicrosoftEnumValue>;
 def err_enumerator_wrapped : Error<
   "enumerator value %0 is not representable in the underlying type %1">;
 def err_enum_redeclare_type_mismatch : Error<
@@ -4961,6 +4958,9 @@
 def ext_enum_value_not_int : Extension<
   "ISO C restricts enumerator values to range of 'int' (%0 is too "
   "%select{small|large}1)">;
+def warn_ms_enum_value_not_int : Extension<
+  "the Microsoft ABI restricts unfixed enumerator values to the range of 'int' "
+  "(%0 is too %select{small|large}1)">, InGroup<MicrosoftEnumValue>;
 def ext_enum_too_large : ExtWarn<
   "enumeration values exceed range of largest integer">, InGroup<EnumTooLarge>;
 def ext_enumerator_increment_too_large : ExtWarn<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to