Author: Aaron Ballman
Date: 2025-04-29T07:06:08-04:00
New Revision: df267d77f6cc06608d2fabc2139cabbd99007381

URL: 
https://github.com/llvm/llvm-project/commit/df267d77f6cc06608d2fabc2139cabbd99007381
DIFF: 
https://github.com/llvm/llvm-project/commit/df267d77f6cc06608d2fabc2139cabbd99007381.diff

LOG: [C] Add new -Wimplicit-int-enum-cast to -Wc++-compat (#137658)

This introduces a new diagnostic group to diagnose implicit casts from
int to an enumeration type. In C, this is valid, but it is not
compatible with C++.

Additionally, this moves the "implicit conversion from enum type to
different enum type" diagnostic from `-Wenum-conversion` to a new group
`-Wimplicit-enum-enum-cast`, which is a more accurate home for it.
`-Wimplicit-enum-enum-cast` is also under `-Wimplicit-int-enum-cast`, as
it is the same incompatibility (the enumeration on the right-hand is
promoted to `int`, so it's an int -> enum conversion).

Fixes #37027

Added: 
    clang/test/Sema/implicit-int-enum-conversion.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaChecking.cpp
    clang/test/Misc/warning-flags-enabled.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0fc46fe10b585..fd873a302a308 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -150,6 +150,15 @@ C Language Changes
 - Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
   diagnoses implicit conversion from ``void *`` to another pointer type as
   being incompatible with C++. (#GH17792)
+- Added ``-Wimplicit-int-enum-cast``, grouped under ``-Wc++-compat``, which
+  diagnoses implicit conversion from integer types to an enumeration type in C,
+  which is not compatible with C++. #GH37027
+- Split "implicit conversion from enum type to 
diff erent enum type" diagnostic
+  from ``-Wenum-conversion`` into its own diagnostic group,
+  ``-Wimplicit-enum-enum-cast``, which is grouped under both
+  ``-Wenum-conversion`` and ``-Wimplicit-int-enum-cast``. This conversion is an
+  int-to-enum conversion because the enumeration on the right-hand side is
+  promoted to ``int`` before the assignment.
 
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 31e2cfa7ab485..97ed38d71ed51 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -103,10 +103,12 @@ def AnonEnumEnumConversion : 
DiagGroup<"anon-enum-enum-conversion",
                                    [DeprecatedAnonEnumEnumConversion]>;
 def EnumEnumConversion : DiagGroup<"enum-enum-conversion",
                                    [DeprecatedEnumEnumConversion]>;
+def ImplicitEnumEnumCast : DiagGroup<"implicit-enum-enum-cast">;
 def EnumFloatConversion : DiagGroup<"enum-float-conversion",
                                     [DeprecatedEnumFloatConversion]>;
 def EnumConversion : DiagGroup<"enum-conversion",
                                [EnumEnumConversion,
+                                ImplicitEnumEnumCast,
                                 EnumFloatConversion,
                                 EnumCompareConditional]>;
 def DeprecatedOFast : DiagGroup<"deprecated-ofast">;
@@ -157,7 +159,10 @@ def : DiagGroup<"c2x-compat", [C23Compat]>;
 def DefaultConstInitUnsafe : DiagGroup<"default-const-init-unsafe">;
 def DefaultConstInit : DiagGroup<"default-const-init", 
[DefaultConstInitUnsafe]>;
 def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
-def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, 
DefaultConstInit]>;
+def ImplicitIntToEnumCast : DiagGroup<"implicit-int-enum-cast",
+                                      [ImplicitEnumEnumCast]>;
+def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit,
+                                        ImplicitIntToEnumCast]>;
 
 def ExternCCompat : DiagGroup<"extern-c-compat">;
 def KeywordCompat : DiagGroup<"keyword-compat">;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4c96142e28134..2e148e01d6e5e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4310,7 +4310,10 @@ def warn_impcast_string_literal_to_bool : Warning<
   InGroup<StringConversion>, DefaultIgnore;
 def warn_impcast_
diff erent_enum_types : Warning<
   "implicit conversion from enumeration type %0 to 
diff erent enumeration type "
-  "%1">, InGroup<EnumConversion>;
+  "%1">, InGroup<ImplicitEnumEnumCast>;
+def warn_impcast_int_to_enum : Warning<
+  "implicit conversion from %0 to enumeration type %1 is invalid in C++">,
+  InGroup<ImplicitIntToEnumCast>, DefaultIgnore;
 def warn_impcast_bool_to_null_pointer : Warning<
     "initialization of pointer of type %0 to null from a constant boolean "
     "expression">, InGroup<BoolConversion>;

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2811fd3a04377..018d121ad8ab5 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12268,13 +12268,19 @@ void Sema::CheckImplicitConversion(Expr *E, QualType 
T, SourceLocation CC,
       *ICContext = true;
     }
 
-    return DiagnoseImpCast(*this, E, T, CC, DiagID);
+    DiagnoseImpCast(*this, E, T, CC, DiagID);
   }
 
+  // If we're implicitly converting from an integer into an enumeration, that
+  // is valid in C but invalid in C++.
+  QualType SourceType = E->getEnumCoercedType(Context);
+  const BuiltinType *CoercedSourceBT = SourceType->getAs<BuiltinType>();
+  if (CoercedSourceBT && CoercedSourceBT->isInteger() && isa<EnumType>(Target))
+    return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_int_to_enum);
+
   // Diagnose conversions between 
diff erent enumeration types.
   // In C, we pretend that the type of an EnumConstantDecl is its enumeration
   // type, to give us better diagnostics.
-  QualType SourceType = E->getEnumCoercedType(Context);
   Source = Context.getCanonicalType(SourceType).getTypePtr();
 
   if (const EnumType *SourceEnum = Source->getAs<EnumType>())

diff  --git a/clang/test/Misc/warning-flags-enabled.c 
b/clang/test/Misc/warning-flags-enabled.c
index 9f210674b126e..5fea199f3feec 100644
--- a/clang/test/Misc/warning-flags-enabled.c
+++ b/clang/test/Misc/warning-flags-enabled.c
@@ -26,13 +26,15 @@
 // CHECK-NO-LEVELS-NOT: {{^F }}
 // CHECK-NO-LEVELS: warn_objc_root_class_missing [-Wobjc-root-class]
 
-// Test if EnumConversion is a subgroup of -Wconversion.
+// Test if EnumConversion is a subgroup of -Wconversion. Because no diagnostics
+// are grouped directly under -Wenum-conversion, we check for
+// -Wimplicit-enum-enum-cast instead (which is itself under -Wenum-conversion).
 // RUN: diagtool show-enabled --no-levels -Wno-conversion -Wenum-conversion %s 
| FileCheck --check-prefix CHECK-ENUM-CONVERSION %s
 // RUN: diagtool show-enabled --no-levels %s | FileCheck --check-prefix 
CHECK-ENUM-CONVERSION %s
 // RUN: diagtool show-enabled --no-levels -Wno-conversion %s | FileCheck 
--check-prefix CHECK-NO-ENUM-CONVERSION %s
 //
-// CHECK-ENUM-CONVERSION: -Wenum-conversion
-// CHECK-NO-ENUM-CONVERSION-NOT: -Wenum-conversion
+// CHECK-ENUM-CONVERSION: -Wimplicit-enum-enum-cast
+// CHECK-NO-ENUM-CONVERSION-NOT: -Wimplicit-enum-enum-cast
 
 // Test if -Wshift-op-parentheses is a subgroup of -Wparentheses
 // RUN: diagtool show-enabled --no-levels -Wno-parentheses 
-Wshift-op-parentheses %s | FileCheck --check-prefix CHECK-SHIFT-OP-PARENTHESES 
%s

diff  --git a/clang/test/Sema/implicit-int-enum-conversion.c 
b/clang/test/Sema/implicit-int-enum-conversion.c
new file mode 100644
index 0000000000000..13afb5d297aba
--- /dev/null
+++ b/clang/test/Sema/implicit-int-enum-conversion.c
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wimplicit-int-enum-cast %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-compat %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
+// RUN: %clang_cc1 -fsyntax-only -verify=good -Wno-implicit-enum-enum-cast %s
+// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat 
-Wno-implicit-enum-enum-cast -Wno-implicit-int-enum-cast %s
+// good-no-diagnostics
+
+enum E1 {
+  E1_Zero,
+  E1_One
+};
+
+enum E2 {
+  E2_Zero
+};
+
+struct S {
+  enum E1 e;
+} s = { 12 }; // expected-warning {{implicit conversion from 'int' to 
enumeration type 'enum E1' is invalid in C++}} \
+                 cxx-error {{cannot initialize a member subobject of type 
'enum E1' with an rvalue of type 'int'}}
+
+enum E1 foo(void) {
+  int x;
+  enum E1 e = 12; // expected-warning {{implicit conversion from 'int' to 
enumeration type 'enum E1' is invalid in C++}} \
+                     cxx-error {{cannot initialize a variable of type 'enum 
E1' with an rvalue of type 'int'}}
+
+  // Enum to integer is fine.
+  x = e;
+
+  // Integer to enum is not fine.
+  e = x;    // expected-warning {{implicit conversion from 'int' to 
enumeration type 'enum E1' is invalid in C++}} \
+               cxx-error {{assigning to 'enum E1' from incompatible type 
'int'}}
+  return x; // expected-warning {{implicit conversion from 'int' to 
enumeration type 'enum E1' is invalid in C++}} \
+               cxx-error {{cannot initialize return object of type 'enum E1' 
with an lvalue of type 'int'}}
+}
+
+// Returning with the correct types is fine.
+enum E1 bar(void) {
+  return E1_Zero;
+}
+
+// Enum to 
diff erent-enum conversion is also a C++ incompatibility, but is
+// handled via a more general diagnostic, -Wimplicit-enum-enum-cast, which is
+// on by default.
+enum E1 quux(void) {
+  enum E1 e1 = E2_Zero; // expected-warning {{implicit conversion from 
enumeration type 'enum E2' to 
diff erent enumeration type 'enum E1'}} \
+                           cxx-error {{cannot initialize a variable of type 
'enum E1' with an rvalue of type 'E2'}}
+  e1 = E2_Zero;         // expected-warning {{implicit conversion from 
enumeration type 'enum E2' to 
diff erent enumeration type 'enum E1'}}   \
+                           cxx-error {{assigning to 'enum E1' from 
incompatible type 'E2'}}
+  return E2_Zero;       // expected-warning {{implicit conversion from 
enumeration type 'enum E2' to 
diff erent enumeration type 'enum E1'}} \
+                           cxx-error {{cannot initialize return object of type 
'enum E1' with an rvalue of type 'E2'}}
+}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to