Skip to content

Commit 8c2ae42

Browse files
authored
[Clang][Sema] Fix missing warning when comparing mismatched enums in … (#81418)
…C mode Factored logic from `CheckImplicitConversion` into new methods `Expr::getEnumConstantDecl` and `Expr::getEnumCoercedType` for use in `checkEnumArithmeticConversions`. Fix #29217
1 parent 2e39b57 commit 8c2ae42

9 files changed

+84
-13
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ Improvements to Clang's time-trace
197197

198198
Bug Fixes in This Version
199199
-------------------------
200+
- Fixed missing warnings when comparing mismatched enumeration constants
201+
in C (`#29217 <https://github.com/llvm/llvm-project/issues/29217>`).
202+
200203
- Clang now accepts elaborated-type-specifiers that explicitly specialize
201204
a member class template for an implicit instantiation of a class template.
202205

clang/include/clang/AST/Expr.h

+13
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ class Expr : public ValueStmt {
153153
TR = t;
154154
}
155155

156+
/// If this expression is an enumeration constant, return the
157+
/// enumeration type under which said constant was declared.
158+
/// Otherwise return the expression's type.
159+
/// Note this effectively circumvents the weak typing of C's enum constants
160+
QualType getEnumCoercedType(const ASTContext &Ctx) const;
161+
156162
ExprDependence getDependence() const {
157163
return static_cast<ExprDependence>(ExprBits.Dependent);
158164
}
@@ -471,6 +477,13 @@ class Expr : public ValueStmt {
471477
/// bit-fields, but it will return null for a conditional bit-field.
472478
FieldDecl *getSourceBitField();
473479

480+
/// If this expression refers to an enum constant, retrieve its declaration
481+
EnumConstantDecl *getEnumConstantDecl();
482+
483+
const EnumConstantDecl *getEnumConstantDecl() const {
484+
return const_cast<Expr *>(this)->getEnumConstantDecl();
485+
}
486+
474487
const FieldDecl *getSourceBitField() const {
475488
return const_cast<Expr*>(this)->getSourceBitField();
476489
}

clang/lib/AST/Expr.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ namespace {
263263
}
264264
}
265265

266+
QualType Expr::getEnumCoercedType(const ASTContext &Ctx) const {
267+
if (isa<EnumType>(this->getType()))
268+
return this->getType();
269+
else if (const auto *ECD = this->getEnumConstantDecl())
270+
return Ctx.getTypeDeclType(cast<EnumDecl>(ECD->getDeclContext()));
271+
return this->getType();
272+
}
273+
266274
SourceLocation Expr::getExprLoc() const {
267275
switch (getStmtClass()) {
268276
case Stmt::NoStmtClass: llvm_unreachable("statement without class");
@@ -4098,6 +4106,13 @@ FieldDecl *Expr::getSourceBitField() {
40984106
return nullptr;
40994107
}
41004108

4109+
EnumConstantDecl *Expr::getEnumConstantDecl() {
4110+
Expr *E = this->IgnoreParenImpCasts();
4111+
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
4112+
return dyn_cast<EnumConstantDecl>(DRE->getDecl());
4113+
return nullptr;
4114+
}
4115+
41014116
bool Expr::refersToVectorElement() const {
41024117
// FIXME: Why do we not just look at the ObjectKind here?
41034118
const Expr *E = this->IgnoreParens();

clang/lib/Sema/SemaChecking.cpp

+2-9
Original file line numberDiff line numberDiff line change
@@ -16148,15 +16148,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1614816148
// Diagnose conversions between different enumeration types.
1614916149
// In C, we pretend that the type of an EnumConstantDecl is its enumeration
1615016150
// type, to give us better diagnostics.
16151-
QualType SourceType = E->getType();
16152-
if (!S.getLangOpts().CPlusPlus) {
16153-
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
16154-
if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
16155-
EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext());
16156-
SourceType = S.Context.getTypeDeclType(Enum);
16157-
Source = S.Context.getCanonicalType(SourceType).getTypePtr();
16158-
}
16159-
}
16151+
QualType SourceType = E->getEnumCoercedType(S.Context);
16152+
Source = S.Context.getCanonicalType(SourceType).getTypePtr();
1616016153

1616116154
if (const EnumType *SourceEnum = Source->getAs<EnumType>())
1616216155
if (const EnumType *TargetEnum = Target->getAs<EnumType>())

clang/lib/Sema/SemaExpr.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,8 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS,
14971497
//
14981498
// Warn on this in all language modes. Produce a deprecation warning in C++20.
14991499
// Eventually we will presumably reject these cases (in C++23 onwards?).
1500-
QualType L = LHS->getType(), R = RHS->getType();
1500+
QualType L = LHS->getEnumCoercedType(S.Context),
1501+
R = RHS->getEnumCoercedType(S.Context);
15011502
bool LEnum = L->isUnscopedEnumerationType(),
15021503
REnum = R->isUnscopedEnumerationType();
15031504
bool IsCompAssign = ACK == Sema::ACK_CompAssign;

clang/test/Sema/builtins-elementwise-math.c

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ void test_builtin_elementwise_add_sat(int i, short s, double d, float4 v, int3 i
7676

7777
enum f { three };
7878
enum f x = __builtin_elementwise_add_sat(one, three);
79+
// expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
7980

8081
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
8182
ext = __builtin_elementwise_add_sat(ext, ext);
@@ -134,6 +135,7 @@ void test_builtin_elementwise_sub_sat(int i, short s, double d, float4 v, int3 i
134135

135136
enum f { three };
136137
enum f x = __builtin_elementwise_sub_sat(one, three);
138+
// expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
137139

138140
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
139141
ext = __builtin_elementwise_sub_sat(ext, ext);
@@ -189,6 +191,7 @@ void test_builtin_elementwise_max(int i, short s, double d, float4 v, int3 iv, u
189191

190192
enum f { three };
191193
enum f x = __builtin_elementwise_max(one, three);
194+
// expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
192195

193196
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
194197
ext = __builtin_elementwise_max(ext, ext);
@@ -244,6 +247,7 @@ void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, u
244247

245248
enum f { three };
246249
enum f x = __builtin_elementwise_min(one, three);
250+
// expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
247251

248252
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
249253
ext = __builtin_elementwise_min(ext, ext);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
2+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
3+
4+
typedef enum EnumA {
5+
A
6+
} EnumA;
7+
8+
enum EnumB {
9+
B
10+
};
11+
12+
enum {
13+
C
14+
};
15+
16+
void foo(void) {
17+
enum EnumA a = A;
18+
enum EnumB b = B;
19+
A == B;
20+
// expected-warning@-1 {{comparison of different enumeration types}}
21+
a == (B);
22+
// expected-warning@-1 {{comparison of different enumeration types}}
23+
a == b;
24+
// expected-warning@-1 {{comparison of different enumeration types}}
25+
A > B;
26+
// expected-warning@-1 {{comparison of different enumeration types}}
27+
A >= b;
28+
// expected-warning@-1 {{comparison of different enumeration types}}
29+
a > b;
30+
// expected-warning@-1 {{comparison of different enumeration types}}
31+
(A) <= ((B));
32+
// expected-warning@-1 {{comparison of different enumeration types}}
33+
a < B;
34+
// expected-warning@-1 {{comparison of different enumeration types}}
35+
a < b;
36+
// expected-warning@-1 {{comparison of different enumeration types}}
37+
38+
// In the following cases we purposefully differ from GCC and dont warn
39+
a == C;
40+
A < C;
41+
b >= C;
42+
}

clang/test/Sema/warn-conditional-emum-types-mismatch.c renamed to clang/test/Sema/warn-conditional-enum-types-mismatch.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ int get_flag(int cond) {
2121
#ifdef __cplusplus
2222
// expected-warning@-2 {{conditional expression between different enumeration types ('ro' and 'rw')}}
2323
#else
24-
// expected-no-diagnostics
24+
// expected-warning@-4 {{conditional expression between different enumeration types ('enum ro' and 'enum rw')}}
2525
#endif
2626
}
2727

clang/test/Sema/warn-overlap.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare %s
2-
// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis %s
1+
// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare -Wno-enum-compare %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis -Wno-enum-compare %s
33

44
#define mydefine 2
55

0 commit comments

Comments
 (0)