-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[Clang][Sema] Fix missing warning when comparing mismatched enums in … #81418
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
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write If you have received no comments on your PR for a week, you can request a review If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang Author: None (44-2-Kupa-Martin) Changes…C mode Factored logic from Fix #29217 Full diff: https://github.com/llvm/llvm-project/pull/81418.diff 9 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ece6013f672621..00ddf0b9656a31 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -161,6 +161,9 @@ Improvements to Clang's time-trace
Bug Fixes in This Version
-------------------------
+- Fixed missing warnings when comparing mismatched enumeration constants
+ in C (`#29217 <https://github.com/llvm/llvm-project/issues/29217>`).
+
- Clang now accepts elaborated-type-specifiers that explicitly specialize
a member class template for an implicit instantiation of a class template.
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 3fc481a62a78a9..bf0622bdeca30e 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -153,6 +153,12 @@ class Expr : public ValueStmt {
TR = t;
}
+ /// If this expression is an enumeration constant, return the
+ /// enumeration type under which said constant was declared.
+ /// Otherwise return the expression's type.
+ /// Note this effectively circumvents the weak typing of C's enum constants
+ QualType getEnumCoercedType(const ASTContext &Ctx) const;
+
ExprDependence getDependence() const {
return static_cast<ExprDependence>(ExprBits.Dependent);
}
@@ -471,6 +477,13 @@ class Expr : public ValueStmt {
/// bit-fields, but it will return null for a conditional bit-field.
FieldDecl *getSourceBitField();
+ /// If this expression refers to an enum constant, retrieve its declaration
+ EnumConstantDecl *getEnumConstantDecl();
+
+ const EnumConstantDecl *getEnumConstantDecl() const {
+ return const_cast<Expr *>(this)->getEnumConstantDecl();
+ }
+
const FieldDecl *getSourceBitField() const {
return const_cast<Expr*>(this)->getSourceBitField();
}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 8b10e289583260..78a0aba5abc3f1 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -263,6 +263,14 @@ namespace {
}
}
+QualType Expr::getEnumCoercedType(const ASTContext &Ctx) const {
+ bool NotEnumType = dyn_cast<EnumType>(this->getType()) == nullptr;
+ if (NotEnumType)
+ if (const EnumConstantDecl *ECD = this->getEnumConstantDecl())
+ return Ctx.getTypeDeclType(cast<EnumDecl>(ECD->getDeclContext()));
+ return this->getType();
+}
+
SourceLocation Expr::getExprLoc() const {
switch (getStmtClass()) {
case Stmt::NoStmtClass: llvm_unreachable("statement without class");
@@ -4097,6 +4105,14 @@ FieldDecl *Expr::getSourceBitField() {
return nullptr;
}
+EnumConstantDecl *Expr::getEnumConstantDecl() {
+ Expr *E = this->IgnoreParenImpCasts();
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
+ return ECD;
+ return nullptr;
+}
+
bool Expr::refersToVectorElement() const {
// FIXME: Why do we not just look at the ObjectKind here?
const Expr *E = this->IgnoreParens();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 71e6e7230fc455..1a869e16c987f4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -16050,15 +16050,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
// 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->getType();
- if (!S.getLangOpts().CPlusPlus) {
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
- EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext());
- SourceType = S.Context.getTypeDeclType(Enum);
- Source = S.Context.getCanonicalType(SourceType).getTypePtr();
- }
- }
+ QualType SourceType = E->getEnumCoercedType(S.Context);
+ Source = S.Context.getCanonicalType(SourceType).getTypePtr();
if (const EnumType *SourceEnum = Source->getAs<EnumType>())
if (const EnumType *TargetEnum = Target->getAs<EnumType>())
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4049ab3bf6cafb..2b6accfd4a4d50 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1497,7 +1497,8 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS,
//
// Warn on this in all language modes. Produce a deprecation warning in C++20.
// Eventually we will presumably reject these cases (in C++23 onwards?).
- QualType L = LHS->getType(), R = RHS->getType();
+ QualType L = LHS->getEnumCoercedType(S.Context),
+ R = RHS->getEnumCoercedType(S.Context);
bool LEnum = L->isUnscopedEnumerationType(),
REnum = R->isUnscopedEnumerationType();
bool IsCompAssign = ACK == Sema::ACK_CompAssign;
diff --git a/clang/test/Sema/builtins-elementwise-math.c b/clang/test/Sema/builtins-elementwise-math.c
index e7b36285fa7dcf..2e05337273ee41 100644
--- a/clang/test/Sema/builtins-elementwise-math.c
+++ b/clang/test/Sema/builtins-elementwise-math.c
@@ -76,6 +76,7 @@ void test_builtin_elementwise_add_sat(int i, short s, double d, float4 v, int3 i
enum f { three };
enum f x = __builtin_elementwise_add_sat(one, three);
+ // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
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
enum f { three };
enum f x = __builtin_elementwise_sub_sat(one, three);
+ // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
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
enum f { three };
enum f x = __builtin_elementwise_max(one, three);
+ // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
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
enum f { three };
enum f x = __builtin_elementwise_min(one, three);
+ // expected-warning@-1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
ext = __builtin_elementwise_min(ext, ext);
diff --git a/clang/test/Sema/warn-compare-enum-types-mismatch.c b/clang/test/Sema/warn-compare-enum-types-mismatch.c
new file mode 100644
index 00000000000000..1c50f35516353f
--- /dev/null
+++ b/clang/test/Sema/warn-compare-enum-types-mismatch.c
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
+
+typedef enum EnumA {
+ A
+} EnumA;
+
+enum EnumB {
+ B
+};
+
+enum {
+ C
+};
+
+void foo(void) {
+ enum EnumA a = A;
+ enum EnumB b = B;
+ A == B;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ a == (B);
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ a == b;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ A > B;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ A >= b;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ a > b;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ (A) <= ((B));
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ a < B;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+ a < b;
+ // expected-warning@-1 {{comparison of different enumeration types}}
+
+ // In the following cases we purposefully differ from GCC and dont warn
+ a == C;
+ A < C;
+ b >= C;
+}
\ No newline at end of file
diff --git a/clang/test/Sema/warn-conditional-emum-types-mismatch.c b/clang/test/Sema/warn-conditional-enum-types-mismatch.c
similarity index 88%
rename from clang/test/Sema/warn-conditional-emum-types-mismatch.c
rename to clang/test/Sema/warn-conditional-enum-types-mismatch.c
index c9e2eddc7764bd..f039245b6fabe5 100644
--- a/clang/test/Sema/warn-conditional-emum-types-mismatch.c
+++ b/clang/test/Sema/warn-conditional-enum-types-mismatch.c
@@ -21,7 +21,7 @@ int get_flag(int cond) {
#ifdef __cplusplus
// expected-warning@-2 {{conditional expression between different enumeration types ('ro' and 'rw')}}
#else
- // expected-no-diagnostics
+ // expected-warning@-4 {{conditional expression between different enumeration types ('enum ro' and 'enum rw')}}
#endif
}
diff --git a/clang/test/Sema/warn-overlap.c b/clang/test/Sema/warn-overlap.c
index 1eddfd1077fd92..2db07ebcd17b87 100644
--- a/clang/test/Sema/warn-overlap.c
+++ b/clang/test/Sema/warn-overlap.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare -Wno-enum-compare %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis -Wno-enum-compare %s
#define mydefine 2
|
ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not the best one to review this, but some drive-bys
clang/lib/AST/Expr.cpp
Outdated
@@ -263,6 +263,14 @@ namespace { | |||
} | |||
} | |||
|
|||
QualType Expr::getEnumCoercedType(const ASTContext &Ctx) const { | |||
bool NotEnumType = dyn_cast<EnumType>(this->getType()) == nullptr; | |||
if (NotEnumType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (NotEnumType) | |
if (!isa<EnumType>(this->getType())) |
Though, would probably suggest just something like:
if (isa<EnumType>(getType())) return getType();
else if (const EnumConstantDecl *ECD = getEnumConstantDecl())
return Ctx.getTypeDeclType...
return getType();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
clang/lib/AST/Expr.cpp
Outdated
EnumConstantDecl *Expr::getEnumConstantDecl() { | ||
Expr *E = this->IgnoreParenImpCasts(); | ||
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) | ||
if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) | |
return dyn_cast<EnumConstantDecl>(DRE->getDecl()); |
ca5879d
to
f08540e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple more coding standard edits, else LGTM.
clang/lib/AST/Expr.cpp
Outdated
@@ -4097,6 +4105,13 @@ FieldDecl *Expr::getSourceBitField() { | |||
return nullptr; | |||
} | |||
|
|||
EnumConstantDecl *Expr::getEnumConstantDecl() { | |||
Expr *E = this->IgnoreParenImpCasts(); | |||
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) | |
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) |
…C mode Factored logic from `CheckImplicitConversion` into new methods `Expr::getEnumConstantDecl` and `Expr::getEnumCoercedType` for use in `checkEnumArithmeticConversions`. Fix llvm#29217
f08540e
to
706ad23
Compare
If everything looks good could someone please merge this pr? I dont have commit access. |
@Kupa-Martin Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested Please check whether problems have been caused by your change specifically, as How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
For what it's worth, this change adds several instances of |
You're correct, those are intentional. |
This change started to break the following C++26 code:
as within the definition of enum |
@stbergmann is right. @erichkeane How should I proceed? Should I open a revert PR? Or should I open a separate issue? |
For now, unless someone insists, I think we're likely fine to do a follow-up PR to fix that. Make sure to put the same reviewers on, and we'll see if we can review this quickly. |
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418 Fixes llvm#84712
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418 Fixes llvm#84712
The behavior of this warning changed in llvm/llvm-project#81418, specifically for C code. Bug: 328490295 Change-Id: I613768ac057aaa2dbe9fbb9303595fda99bacbf9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5368669 Reviewed-by: Arthur Eubanks <[email protected]> Commit-Queue: Amy Huang <[email protected]> Cr-Commit-Position: refs/heads/main@{#1271874}
…C mode
Factored logic from
CheckImplicitConversion
into new methodsExpr::getEnumConstantDecl
andExpr::getEnumCoercedType
for use incheckEnumArithmeticConversions
.Fix #29217