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