Skip to content

Commit df267d7

Browse files
authored
[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
1 parent ebeae64 commit df267d7

File tree

6 files changed

+84
-7
lines changed

6 files changed

+84
-7
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ C Language Changes
150150
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
151151
diagnoses implicit conversion from ``void *`` to another pointer type as
152152
being incompatible with C++. (#GH17792)
153+
- Added ``-Wimplicit-int-enum-cast``, grouped under ``-Wc++-compat``, which
154+
diagnoses implicit conversion from integer types to an enumeration type in C,
155+
which is not compatible with C++. #GH37027
156+
- Split "implicit conversion from enum type to different enum type" diagnostic
157+
from ``-Wenum-conversion`` into its own diagnostic group,
158+
``-Wimplicit-enum-enum-cast``, which is grouped under both
159+
``-Wenum-conversion`` and ``-Wimplicit-int-enum-cast``. This conversion is an
160+
int-to-enum conversion because the enumeration on the right-hand side is
161+
promoted to ``int`` before the assignment.
153162

154163
C2y Feature Support
155164
^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ def AnonEnumEnumConversion : DiagGroup<"anon-enum-enum-conversion",
103103
[DeprecatedAnonEnumEnumConversion]>;
104104
def EnumEnumConversion : DiagGroup<"enum-enum-conversion",
105105
[DeprecatedEnumEnumConversion]>;
106+
def ImplicitEnumEnumCast : DiagGroup<"implicit-enum-enum-cast">;
106107
def EnumFloatConversion : DiagGroup<"enum-float-conversion",
107108
[DeprecatedEnumFloatConversion]>;
108109
def EnumConversion : DiagGroup<"enum-conversion",
109110
[EnumEnumConversion,
111+
ImplicitEnumEnumCast,
110112
EnumFloatConversion,
111113
EnumCompareConditional]>;
112114
def DeprecatedOFast : DiagGroup<"deprecated-ofast">;
@@ -157,7 +159,10 @@ def : DiagGroup<"c2x-compat", [C23Compat]>;
157159
def DefaultConstInitUnsafe : DiagGroup<"default-const-init-unsafe">;
158160
def DefaultConstInit : DiagGroup<"default-const-init", [DefaultConstInitUnsafe]>;
159161
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
160-
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit]>;
162+
def ImplicitIntToEnumCast : DiagGroup<"implicit-int-enum-cast",
163+
[ImplicitEnumEnumCast]>;
164+
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit,
165+
ImplicitIntToEnumCast]>;
161166

162167
def ExternCCompat : DiagGroup<"extern-c-compat">;
163168
def KeywordCompat : DiagGroup<"keyword-compat">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4310,7 +4310,10 @@ def warn_impcast_string_literal_to_bool : Warning<
43104310
InGroup<StringConversion>, DefaultIgnore;
43114311
def warn_impcast_different_enum_types : Warning<
43124312
"implicit conversion from enumeration type %0 to different enumeration type "
4313-
"%1">, InGroup<EnumConversion>;
4313+
"%1">, InGroup<ImplicitEnumEnumCast>;
4314+
def warn_impcast_int_to_enum : Warning<
4315+
"implicit conversion from %0 to enumeration type %1 is invalid in C++">,
4316+
InGroup<ImplicitIntToEnumCast>, DefaultIgnore;
43144317
def warn_impcast_bool_to_null_pointer : Warning<
43154318
"initialization of pointer of type %0 to null from a constant boolean "
43164319
"expression">, InGroup<BoolConversion>;

clang/lib/Sema/SemaChecking.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12268,13 +12268,19 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
1226812268
*ICContext = true;
1226912269
}
1227012270

12271-
return DiagnoseImpCast(*this, E, T, CC, DiagID);
12271+
DiagnoseImpCast(*this, E, T, CC, DiagID);
1227212272
}
1227312273

12274+
// If we're implicitly converting from an integer into an enumeration, that
12275+
// is valid in C but invalid in C++.
12276+
QualType SourceType = E->getEnumCoercedType(Context);
12277+
const BuiltinType *CoercedSourceBT = SourceType->getAs<BuiltinType>();
12278+
if (CoercedSourceBT && CoercedSourceBT->isInteger() && isa<EnumType>(Target))
12279+
return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_int_to_enum);
12280+
1227412281
// Diagnose conversions between different enumeration types.
1227512282
// In C, we pretend that the type of an EnumConstantDecl is its enumeration
1227612283
// type, to give us better diagnostics.
12277-
QualType SourceType = E->getEnumCoercedType(Context);
1227812284
Source = Context.getCanonicalType(SourceType).getTypePtr();
1227912285

1228012286
if (const EnumType *SourceEnum = Source->getAs<EnumType>())

clang/test/Misc/warning-flags-enabled.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
// CHECK-NO-LEVELS-NOT: {{^F }}
2727
// CHECK-NO-LEVELS: warn_objc_root_class_missing [-Wobjc-root-class]
2828

29-
// Test if EnumConversion is a subgroup of -Wconversion.
29+
// Test if EnumConversion is a subgroup of -Wconversion. Because no diagnostics
30+
// are grouped directly under -Wenum-conversion, we check for
31+
// -Wimplicit-enum-enum-cast instead (which is itself under -Wenum-conversion).
3032
// RUN: diagtool show-enabled --no-levels -Wno-conversion -Wenum-conversion %s | FileCheck --check-prefix CHECK-ENUM-CONVERSION %s
3133
// RUN: diagtool show-enabled --no-levels %s | FileCheck --check-prefix CHECK-ENUM-CONVERSION %s
3234
// RUN: diagtool show-enabled --no-levels -Wno-conversion %s | FileCheck --check-prefix CHECK-NO-ENUM-CONVERSION %s
3335
//
34-
// CHECK-ENUM-CONVERSION: -Wenum-conversion
35-
// CHECK-NO-ENUM-CONVERSION-NOT: -Wenum-conversion
36+
// CHECK-ENUM-CONVERSION: -Wimplicit-enum-enum-cast
37+
// CHECK-NO-ENUM-CONVERSION-NOT: -Wimplicit-enum-enum-cast
3638

3739
// Test if -Wshift-op-parentheses is a subgroup of -Wparentheses
3840
// RUN: diagtool show-enabled --no-levels -Wno-parentheses -Wshift-op-parentheses %s | FileCheck --check-prefix CHECK-SHIFT-OP-PARENTHESES %s
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -Wimplicit-int-enum-cast %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-compat %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wno-implicit-enum-enum-cast %s
5+
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-implicit-enum-enum-cast -Wno-implicit-int-enum-cast %s
6+
// good-no-diagnostics
7+
8+
enum E1 {
9+
E1_Zero,
10+
E1_One
11+
};
12+
13+
enum E2 {
14+
E2_Zero
15+
};
16+
17+
struct S {
18+
enum E1 e;
19+
} s = { 12 }; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
20+
cxx-error {{cannot initialize a member subobject of type 'enum E1' with an rvalue of type 'int'}}
21+
22+
enum E1 foo(void) {
23+
int x;
24+
enum E1 e = 12; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
25+
cxx-error {{cannot initialize a variable of type 'enum E1' with an rvalue of type 'int'}}
26+
27+
// Enum to integer is fine.
28+
x = e;
29+
30+
// Integer to enum is not fine.
31+
e = x; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
32+
cxx-error {{assigning to 'enum E1' from incompatible type 'int'}}
33+
return x; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
34+
cxx-error {{cannot initialize return object of type 'enum E1' with an lvalue of type 'int'}}
35+
}
36+
37+
// Returning with the correct types is fine.
38+
enum E1 bar(void) {
39+
return E1_Zero;
40+
}
41+
42+
// Enum to different-enum conversion is also a C++ incompatibility, but is
43+
// handled via a more general diagnostic, -Wimplicit-enum-enum-cast, which is
44+
// on by default.
45+
enum E1 quux(void) {
46+
enum E1 e1 = E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
47+
cxx-error {{cannot initialize a variable of type 'enum E1' with an rvalue of type 'E2'}}
48+
e1 = E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
49+
cxx-error {{assigning to 'enum E1' from incompatible type 'E2'}}
50+
return E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
51+
cxx-error {{cannot initialize return object of type 'enum E1' with an rvalue of type 'E2'}}
52+
}

0 commit comments

Comments
 (0)