-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[C] Add new -Wimplicit-int-enum-cast to -Wc++-compat #137658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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 llvm#37027
@llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesThis 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 Fixes #37027 Full diff: https://github.com/llvm/llvm-project/pull/137658.diff 6 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3105d8b481560..1bcf358974437 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 different 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_different_enum_types : Warning<
"implicit conversion from enumeration type %0 to different 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..332504405eccb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -12271,10 +12271,16 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
return 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 different 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 different-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 different 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 different 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 different enumeration type 'enum E1'}} \
+ cxx-error {{cannot initialize return object of type 'enum E1' with an rvalue of type 'E2'}}
+}
|
@AaronBallman If you try to compile the following c program after this patch
cmd: It will give an incorrect diagnostic:
|
There is an implicit enum conversion happening (paren expression results in an |
Ooof, this may not be easy to work around. In C, we're in a context where we know we have an implicit conversion. But we have no way of knowing that we would not have the implicit conversion in C++. We could probably handle comma expressions as a special case, but I suspect that will just move the issue to another place. Still investigating whether there's a good way to salvage this. |
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 llvm#37027
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 llvm#37027
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 llvm#37027
In C++, the type of an enumerator is the type of the enumeration, whereas in C, the type of the enumerator is 'int'. The type of a comma operator is the type of the right-hand operand, which means you can get an implicit conversion with this code in C but not in C++: enum E { Zero }; enum E foo() { return ((void)0, Zero); } We were previously incorrectly diagnosing this code as being incompatible with C++ because the type of the paren expression would be 'int' there, whereas in C++ the type is 'E'. So now we handle the comma operator with special logic when analyzing implicit conversions in C. When analyzing the left-hand operand of a comma operator, we do not need to check for that operand causing an implicit conversion for the entire comma expression. So we only check for that case with the right-hand operand. This addresses a concern brought up post-commit: llvm#137658 (comment)
#138752 should address this issue, thank you for bringing it up! |
@AaronBallman Thanks a lot for the prompt fix! |
Thank you for promptly letting me know about the problem! |
In C++, the type of an enumerator is the type of the enumeration, whereas in C, the type of the enumerator is 'int'. The type of a comma operator is the type of the right-hand operand, which means you can get an implicit conversion with this code in C but not in C++: ``` enum E { Zero }; enum E foo() { return ((void)0, Zero); } ``` We were previously incorrectly diagnosing this code as being incompatible with C++ because the type of the paren expression would be 'int' there, whereas in C++ the type is 'E'. So now we handle the comma operator with special logic when analyzing implicit conversions in C. When analyzing the left-hand operand of a comma operator, we do not need to check for that operand causing an implicit conversion for the entire comma expression. So we only check for that case with the right-hand operand. This addresses a concern brought up post-commit: #137658 (comment)
…ns (#138752) In C++, the type of an enumerator is the type of the enumeration, whereas in C, the type of the enumerator is 'int'. The type of a comma operator is the type of the right-hand operand, which means you can get an implicit conversion with this code in C but not in C++: ``` enum E { Zero }; enum E foo() { return ((void)0, Zero); } ``` We were previously incorrectly diagnosing this code as being incompatible with C++ because the type of the paren expression would be 'int' there, whereas in C++ the type is 'E'. So now we handle the comma operator with special logic when analyzing implicit conversions in C. When analyzing the left-hand operand of a comma operator, we do not need to check for that operand causing an implicit conversion for the entire comma expression. So we only check for that case with the right-hand operand. This addresses a concern brought up post-commit: llvm/llvm-project#137658 (comment)
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 llvm#37027
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 llvm#37027
@AaronBallman I met another case, giving the same error even after the fix. Sorry I only noticed it now:
Both REG_EBRACE, REG_BADBR are |
Thank you! I can confirm the issue happens; I spent a few hours looking at it this morning and didn't have a solution, but ran out of time I could spend on it. Do you mind filing an issue so we don't lose track of it? |
Created an issue: #139536 |
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 toint
, so it's an int -> enum conversion).Fixes #37027